diff options
-rw-r--r-- | 02-usart/Makefile.preamble | 2 | ||||
-rw-r--r-- | 02-usart/include/arch/arm/arch.h | 1 | ||||
-rw-r--r-- | 02-usart/include/arch/x86_64/arch.h | 2 | ||||
-rw-r--r-- | 02-usart/include/core/dma.h | 18 | ||||
-rw-r--r-- | 02-usart/include/core/isrs.inc | 10 | ||||
-rw-r--r-- | 02-usart/include/core/rcc.h | 3 | ||||
-rw-r--r-- | 02-usart/include/core/usart.h | 4 | ||||
-rw-r--r-- | 02-usart/include/peri/dma.h | 279 | ||||
-rw-r--r-- | 02-usart/src/main.c | 40 | ||||
-rw-r--r-- | 02-usart/src/peri/dma.c | 312 | ||||
-rw-r--r-- | 02-usart/test_harness/fake_env.c | 3 | ||||
-rw-r--r-- | 02-usart/test_harness/fake_env.h | 1 | ||||
-rw-r--r-- | 02-usart/test_harness/test_harness.c | 66 | ||||
-rw-r--r-- | 02-usart/test_harness/test_harness.h | 38 | ||||
-rw-r--r-- | 02-usart/tests/test_dma.c | 147 |
15 files changed, 868 insertions, 58 deletions
diff --git a/02-usart/Makefile.preamble b/02-usart/Makefile.preamble index 0dc02bb..486bd67 100644 --- a/02-usart/Makefile.preamble +++ b/02-usart/Makefile.preamble @@ -18,6 +18,8 @@ flash: _$(PREFIX)_obs/main.bin clean: rm -rf _*_obs + rm -rf tests/build + rm -rf test_harness/*.a test_harness/*.o genmake: ./genmake.pl > Makefile diff --git a/02-usart/include/arch/arm/arch.h b/02-usart/include/arch/arm/arch.h index 175c984..569ca77 100644 --- a/02-usart/include/arch/arm/arch.h +++ b/02-usart/include/arch/arm/arch.h @@ -26,6 +26,7 @@ #define SYSTEM_CONFIG_BLOCK_BASE (0xE000E008) #define NVIC_BASE (0xE000E004) +#define RCC_BASE (0x40021000) #include <stdint.h> #ifndef DRY_RUN diff --git a/02-usart/include/arch/x86_64/arch.h b/02-usart/include/arch/x86_64/arch.h index a93425d..258214e 100644 --- a/02-usart/include/arch/x86_64/arch.h +++ b/02-usart/include/arch/x86_64/arch.h @@ -6,6 +6,8 @@ #define ARCH_PC #define enable_all_interrupts() do {} while(0) +#define RCC_BASE (load_fake_rcc__()) + #define DMA1_BASE (load_fake_ahb1__() + 0x0) #define DMA2_BASE (load_fake_ahb1__() + 0x400) diff --git a/02-usart/include/core/dma.h b/02-usart/include/core/dma.h index 73bca76..8e4896d 100644 --- a/02-usart/include/core/dma.h +++ b/02-usart/include/core/dma.h @@ -28,7 +28,7 @@ typedef enum { DMA_PRIORITY_LEVEL_MEDIUM = 1, DMA_PRIORITY_LEVEL_HIGH = 2, DMA_PRIORITY_LEVEL_VERY_HIGH = 3 -} dma_priority_level; +} dma_priority_level_t; typedef enum { READ_FROM_PERIPHERAL = 0, @@ -48,7 +48,7 @@ typedef struct { #define dma_cc_psize (3 << 8) // Peripheral size #define dma_cc_msize (3 << 10) // Memory size #define dma_cc_pl (3 << 12) // Priority level -#define dma_cc_mem2mem (1 << 13) // Memory to memory mode +#define dma_cc_mem2mem (1 << 14) // Memory to memory mode __IO uint32_t cc_r; @@ -134,13 +134,13 @@ typedef struct { __IO uint32_t reserved[5]; /* DMA channel selection register. */ -#define c1s (0xF << 0) // DMA channel 1 selection. -#define c2s (0xF << 4) // DMA channel 2 selection. -#define c3s (0xF << 8) // DMA channel 3 selection. -#define c4s (0xF << 12) // DMA channel 4 selection. -#define c5s (0xF << 16) // DMA channel 5 selection. -#define c6s (0xF << 20) // DMA channel 6 selection. -#define c7s (0xF << 24) // DMA channel 7 selection. +#define dma_c1s (0xF << 0) // DMA channel 1 selection. +#define dma_c2s (0xF << 4) // DMA channel 2 selection. +#define dma_c3s (0xF << 8) // DMA channel 3 selection. +#define dma_c4s (0xF << 12) // DMA channel 4 selection. +#define dma_c5s (0xF << 16) // DMA channel 5 selection. +#define dma_c6s (0xF << 20) // DMA channel 6 selection. +#define dma_c7s (0xF << 24) // DMA channel 7 selection. __IO uint32_t csel_r; } dma_t; diff --git a/02-usart/include/core/isrs.inc b/02-usart/include/core/isrs.inc index e45e216..0682238 100644 --- a/02-usart/include/core/isrs.inc +++ b/02-usart/include/core/isrs.inc @@ -75,11 +75,11 @@ IRQ(on_uart4, UART4, 68) IRQ(on_uart5, UART5, 69) IRQ(on_tim6_dacunder, TIM6_DACUNDER, 70) IRQ(on_tim7, TIM7, 71) -IRQ(on_dma2_channel1, DMA2_CHANNEL1, 72) -IRQ(on_dma2_channel2, DMA2_CHANNEL2, 73) -IRQ(on_dma2_channel3, DMA2_CHANNEL3, 74) -IRQ(on_dma2_channel4, DMA2_CHANNEL4, 75) -IRQ(on_dma2_channel5, DMA2_CHANNEL5, 76) +IRQ(on_dma2_channel1, DMA2_CHANNEL1_IRQ, 72) +IRQ(on_dma2_channel2, DMA2_CHANNEL2_IRQ, 73) +IRQ(on_dma2_channel3, DMA2_CHANNEL3_IRQ, 74) +IRQ(on_dma2_channel4, DMA2_CHANNEL4_IRQ, 75) +IRQ(on_dma2_channel5, DMA2_CHANNEL5_IRQ, 76) IRQ(on_dfsdm1_flt0, DFSDM1_FLT0, 77) IRQ(on_dfsdm1_flt1, DFSDM1_FLT1, 78) IRQ(on_dfsdm1_flt2, DFSDM1_FLT2, 79) diff --git a/02-usart/include/core/rcc.h b/02-usart/include/core/rcc.h index 9c82501..45f64b5 100644 --- a/02-usart/include/core/rcc.h +++ b/02-usart/include/core/rcc.h @@ -1,11 +1,10 @@ #ifndef H__RCC_ #define H__RCC_ +#include "arch.h" #include "common.h" #include <stdint.h> -#define RCC_BASE ((uint32_t)0x40021000) - typedef struct { __IO uint32_t c_r; /* Clock control register. 0x00 */ __IO uint32_t icsc_r; /* Internal clock srcs calibration register. 0x04 */ diff --git a/02-usart/include/core/usart.h b/02-usart/include/core/usart.h index 667b931..8d841df 100644 --- a/02-usart/include/core/usart.h +++ b/02-usart/include/core/usart.h @@ -8,8 +8,8 @@ #include "rcc.h" #include <assert.h> -#define USART1 (* (__IO usart_t*) USART1_BASE) -#define USART2 (* (__IO usart_t*) USART2_BASE) +#define USART1 (* (usart_t*) USART1_BASE) +#define USART2 (* (usart_t*) USART2_BASE) /* * Possible USART clock sources. diff --git a/02-usart/include/peri/dma.h b/02-usart/include/peri/dma.h new file mode 100644 index 0000000..d3cac4b --- /dev/null +++ b/02-usart/include/peri/dma.h @@ -0,0 +1,279 @@ +#ifndef PERI_DMA_H_ +#define PERI_DMA_H_ + +#include "common.h" +#include "core/dma.h" /* Access to the DMA registers. */ +#include "core/irq.h" + +#define DMA_ERROR_CHANNEL_IN_USE 1 + +#define CAT2(x, y) x ## y +#define CAT1(v, c) CAT2(v, c) +#define DMA_RESERVED(dma) CAT1(dma ## _PERIPH_RESERVED, __COUNTER__) + +#define ALTERNATE0 0x0000 +#define ALTERNATE1 0x0100 +#define ALTERNATE2 0x0200 +#define ALTERNATE3 0x0300 + +#define DMA_N_CHANNELS 7 +typedef enum { + DMA1_PERIPH_ADC1 = 0, + DMA1_PERIPH_ADC2 = 1, + DMA1_PERIPH_ADC3 = 2, + DMA1_PERIPH_DFSDM1_FLT0 = 3, + DMA1_PERIPH_DFSDM1_FLT1 = 4, + DMA1_PERIPH_DFSDM1_FLT2 = 5, + DMA1_PERIPH_DFSDM1_FLT3 = 6, + + DMA_RESERVED(DMA1) = 7, + DMA1_PERIPH_SPI1_RX = 8, + DMA1_PERIPH_SPI1_TX = 9, + DMA1_PERIPH_SPI2_RX = 10, + DMA1_PERIPH_SPI2_TX = 11, + DMA1_PERIPH_SAI2_A = 12, + DMA1_PERIPH_SAI2_B = 13, + + DMA_RESERVED(DMA1) = 14, + DMA1_PERIPH_USART3_TX = 15, + DMA1_PERIPH_USART3_RX = 16, + DMA1_PERIPH_USART1_TX = 17, + DMA1_PERIPH_USART1_RX = 18, + DMA1_PERIPH_USART2_RX = 19, + DMA1_PERIPH_USART2_TX = 20, + + DMA_RESERVED(DMA1) = 21, + DMA1_PERIPH_I2C3_TX = 22, + DMA1_PERIPH_I2C3_RX = 23, + DMA1_PERIPH_I2C2_TX = 24, + DMA1_PERIPH_I2C2_RX = 25, + DMA1_PERIPH_I2C1_TX = 26, + DMA1_PERIPH_I2C1_RX = 27, + + DMA1_PERIPH_TIM2_CH3 = 28, + DMA1_PERIPH_TIM2_UP = 29, + DMA1_PERIPH_TIM16_CH1_1 = 30 | ALTERNATE0, + DMA1_PERIPH_TIM16_UP_1 = 30 | ALTERNATE1, /* Same as TIM16_CH1. */ + DMA_RESERVED(DMA1) = 31, + DMA1_PERIPH_TIM2_CH1 = 32, + DMA1_PERIPH_TIM16_CH1_2 = 33, + DMA1_PERIPH_TIM16_UP_2 = 33 | ALTERNATE1, /* Same as TIM16_CH1. */ + DMA1_PERIPH_TIM2_CH2 = 34, + DMA1_PERIPH_TIM2_CH4 = 34 | ALTERNATE1, /* Same as TIM2_CH2. */ + + + DMA1_PERIPH_TIM17_CH1_1 = 35, + DMA1_PERIPH_TIM17_UP_1 = 35 | ALTERNATE1, /* Same as TIM17_CH1 */ + DMA1_PERIPH_TIM3_CH3 = 36, + DMA1_PERIPH_TIM3_CH4 = 37, + DMA1_PERIPH_TIM3_UP = 37 | ALTERNATE1, /* Same as TIM3_CH4 */ + DMA1_PERIPH_TIM7_UP = 38, + DMA1_PERIPH_DAC_CH2 = 38 | ALTERNATE1, /* Same as TIM7_UP */ + DMA1_PERIPH_QUADSPI = 39, + DMA1_PERIPH_TIM3_CH1 = 40, + DMA1_PERIPH_TIM3_TRIG = 40 | ALTERNATE1, /* Same as TIM3_CH1 */ + DMA1_PERIPH_TIM17_CH1_2 = 41, + DMA1_PERIPH_TIM17_UP_2 = 41 | ALTERNATE1, /* Same as TIM17_CH1 */ + + DMA1_PERIPH_TIM4_CH1 = 42, + DMA_RESERVED(DMA1) = 43, + DMA1_PERIPH_TIM6_UP = 44, + DMA1_PERIPH_DAC_CH1 = 44 | ALTERNATE1, /* Same as TIM6_UP */ + DMA1_PERIPH_TIM4_CH2 = 45, + DMA1_PERIPH_TIM4_CH3 = 46, + DMA_RESERVED(DMA1) = 47, + DMA1_PERIPH_TIM4_UP = 48, + + DMA_DMA1_PERIHP_RESERVED5 = 49, + DMA1_PERIPH_TIM1_CH1 = 50, + DMA1_PERIPH_TIM1_CH2 = 51, + DMA1_PERIPH_TIM1_CH4 = 52, + DMA1_PERIPH_TIM1_TRIG = 52 | ALTERNATE1, /* Same as TIM1_TRIG */ + DMA1_PERIPH_TIM1_COM = 52 | ALTERNATE2, /* Same as TIM1_TRIG */ + DMA1_PERIPH_TIM15_CH1 = 53, + DMA1_PERIPH_TIM15_UP = 53 | ALTERNATE1, /* Same as TIM15_CH1 */ + DMA1_PERIPH_TIM15_TRIG = 53 | ALTERNATE2, /* Same as TIM15_CH1 */ + DMA1_PERIPH_TIM15_COM = 53 | ALTERNATE3, /* Same as TIM15_CH1 */ + DMA1_PERIPH_TIM1_UP = 54, + DMA1_PERIPH_TIM1_CH3 = 55, + + DMA2_DMA1_SWITCH__ = 56, + + DMA2_PERIPH_I2C4_RX = 56, + DMA2_PERIPH_I2C4_TX = 57, + DMA2_PERIPH_ADC1 = 58, + DMA2_PERIPH_ADC2 = 59, + DMA2_PERIPH_ADC3 = 60, + DMA2_PERIPH_DCMI_1 = 61, + DMA_RESERVED(DMA2) = 62, + + DMA2_PERIPH_SAI1_A_1 = 63, + DMA2_PERIPH_SAI1_B_1 = 64, + DMA2_PERIPH_SAI2_A = 65, + DMA2_PERIPH_SAI2_B = 66, + DMA_RESERVED(DMA2) = 67, + DMA2_PERIPH_SAI1_A_2 = 68, + DMA2_PERIPH_SAI1_B_2 = 69, + + DMA2_PERIPH_UART5_TX = 70, + DMA2_PERIPH_UART5_RX = 71, + DMA2_PERIPH_UART4_TX = 72, + DMA_RESERVED(DMA2) = 73, + DMA2_PERIPH_UART4_RX = 74, + DMA2_PERIPH_USART1_TX = 75, + DMA2_PERIPH_USART1_RX = 76, + + DMA2_PERIPH_SPI3_RX = 77, + DMA2_PERIPH_SPI3_TX = 78, + DMA_RESERVED(DMA2) = 79, + DMA2_PERIPH_TIM6_UP = 80, + DMA2_PERIPH_DAC_CH1 = 80 | ALTERNATE1, /* Same as TIM6_UP */ + DMA2_PERIPH_TIM7_UP = 81, + DMA2_PERIPH_DAC_CH2 = 81 | ALTERNATE1, /* Same as TIM7_UP */ + DMA_RESERVED(DMA2) = 82, + DMA2_PERIPH_QUADSPI = 83, + + DMA2_PERIPH_SWPMI1_RX = 84, + DMA2_PERIPH_SWPMI1_TX = 85, + DMA2_PERIPH_SPI1_RX = 86, + DMA2_PERIPH_SPI1_TX = 87, + DMA2_PERIPH_DCMI_2 = 88, + DMA2_PERIPH_LPUART1_TX = 89, + DMA2_PERIPH_LPUART1_RX = 90, + + + DMA2_PERIPH_TIM5_CH4 = 91, + DMA2_PERIPH_TIM5_TRIG = 91 | ALTERNATE1, /* Same as TIM5_CH4 */ + DMA2_PERIPH_TIM5_CH3 = 92, + DMA2_PERIPH_TIM5_UP = 92 | ALTERNATE1, /* Same as TIM5_CH3 */ + DMA_RESERVED(DMA2) = 93, + DMA2_PERIPH_TIM5_CH2 = 94, + DMA2_PERIPH_TIM5_CH1 = 95, + DMA2_PERIPH_I2C1_RX = 96, + DMA2_PERIPH_I2C1_TX = 97, + + DMA2_PERIPH_AES_IN_1 = 98, + DMA2_PERIPH_AES_OUT_1 = 99, + DMA2_PERIPH_AES_OUT_2 = 100, + DMA_RESERVED(DMA2) = 101, + DMA2_PERIPH_AES_IN_2 = 102, + DMA_RESERVED(DMA2) = 103, + DMA2_PERIPH_HASH_IN = 104, + + DMA2_PERIPH_TIM8_CH3 = 105, + DMA2_PERIPH_TIM8_UP = 105 | ALTERNATE1, /* Same as TIM8_CH3 */ + DMA2_PERIPH_TIM8_CH4 = 106, + DMA2_PERIPH_TIM8_TRIG = 106 | ALTERNATE1, /* Same as TIM8_CH4 */ + DMA2_PERIPH_TIM8_COM = 106 | ALTERNATE2, /* Same as TIM8_CH4 */ + DMA_RESERVED(DMA2) = 107, + DMA2_PERIPH_SDMMC1_1 = 108, + DMA2_PERIPH_SDMMC1_2 = 109, + DMA2_PERIPH_TIM8_CH1 = 110, + DMA2_PERIPH_TIM8_CH2 = 111, + + + DMA_PERIPH_SENTINEL, +} dma_peripheral_t; + + +/* Defines a DMA channel. */ +typedef struct { + uint8_t dma; /* 0 = DMA1, 1 = DMA2 */ + uint8_t chan; /* 0 - 6 */ +} dma_channel_t; + + +/* + * Defines a DMA channel allocated for memory-to-peripheral transfers. This + * structure is only nominally different from dma_channel_t in order to provide + * rudimentary type-checking. + */ +typedef struct { + dma_channel_t c_; +} dma_mem2p_channel_t; + +/* + * Defines a DMA channel allocated for peripheral-to-memory transfers. This + * structure is only nominally different from dma_channel_t in order to provide + * rudimentary type-checking. + */ +typedef struct { + dma_channel_t c_; +} dma_p2mem_channel_t; + +/* Defines a DMA channel allocated for mem2mem transfers. + * This structure is only nominally different from dma_channel_t + * in order to provide rudimentary type-checking. + */ +typedef struct { + dma_channel_t c_; +} dma_mem2mem_channel_t; + +#define DMA_CHAN_ERROR ((dma_channel_t) { .dma = 0xff, .chan = 0xff }) + +typedef struct { + bool transfer_complete_interrupt_enable; + bool half_transfer_interrupt_enable; + bool transfer_error_interrupt_enable; + + bool circular_mode; + bool peripheral_increment; + bool memory_increment; + + dma_size_t peripheral_block_size; + dma_size_t memory_block_size; + + dma_priority_level_t priority; +} dma_opts_t; + +#define DEFAULT_DMA_OPTS \ + ((dma_opts_t) { .memory_increment = 1, \ + .peripheral_increment = 0, \ + .transfer_complete_interrupt_enable = 0, \ + .half_transfer_interrupt_enable = 0, \ + .transfer_error_interrupt_enable = 0, \ + .circular_mode = 0, \ + .peripheral_block_size = DMA_SIZE_8_BITS, \ + .memory_block_size = DMA_SIZE_8_BITS, \ + .priority = DMA_PRIORITY_LEVEL_MEDIUM }) + +dma_p2mem_channel_t select_dma_channel_p2mem( + dma_peripheral_t peripheral, + dma_opts_t* opts_in, + int* error); + +dma_mem2p_channel_t select_dma_channel_mem2p( + dma_peripheral_t peripheral, + dma_opts_t* opts_in, + int* error); + +/* Returns a dma channel used for memory-to-memory transfers. + * + * channel - the channel this dma should use. The channel should + * be on the range [0-13]. The channels [0-6] refer to the 7 channels + * on DMA1, where channels [7-13] refer to the 7 channels on DMA2. + * + * If `channel` is -1, then the highest unused dma channel is selected. + */ +dma_mem2mem_channel_t select_dma_channel_mem2mem( + int channel, + dma_opts_t* opts, + int* error_out); + +void dma_mem2p_initiate_transfer( + dma_mem2p_channel_t chan, const void* from_loc, uint16_t nblocks); + +void dma_p2mem_initiate_transfer( + dma_p2mem_channel_t chan, void* to_loc, uint16_t nblocks); + +void dma_mem2mem_initiate_transfer( + dma_mem2mem_channel_t chan, + void* to_loc, + const void* from_loc, + uint16_t nblocks); + +void release_dma_channel(dma_channel_t chan); + +interrupt_t dma_channel_get_interrupt(dma_channel_t chan); + +#endif diff --git a/02-usart/src/main.c b/02-usart/src/main.c index 8b98cbf..d26dce4 100644 --- a/02-usart/src/main.c +++ b/02-usart/src/main.c @@ -8,6 +8,7 @@ #include "core/nvic.h" #include "core/irq.h" +#include "peri/dma.h" #include "delay.h" #include "mem.h" #include "spin.h" @@ -64,37 +65,30 @@ int main() setup_usart2(115200); regset(USART2.c_r1, usart_txeie, 1); regset(USART2.c_r1, usart_rxneie, 1); + usart_enable_dma(&USART2, USART_ENABLE_TX); usart_set_enabled(&USART2, USART_ENABLE_TX | USART_ENABLE_RX); - enable_interrupt(IRQ_USART2); - USART2.td_r = (uint8_t) 0x61; + dma_opts_t opts = DEFAULT_DMA_OPTS; + opts.transfer_complete_interrupt_enable = 1; + int ec = 0; + dma_mem2p_channel_t dma_chan = + select_dma_channel_mem2p(DMA1_PERIPH_USART2_TX, &opts, &ec); + enable_interrupt(dma_channel_get_interrupt(dma_chan.c_)); + + if (ec) { + usart_printf(&USART2, "Select DMA channel failed :( %d\n", ec); + for (;;); + } + + const char* thing = "HELLO DMA!\r\n"; + regset(USART2.ic_r, usart_tccf, 1); + dma_mem2p_initiate_transfer(dma_chan, thing, strlen(thing)); __IO gpio_port_t* port_b = enable_gpio(GPIO_PORT_B); gpio_output_pin_t pin3 = set_gpio_pin_output(port_b, PIN_3); pin_on(pin3); - usart_printf(&USART2, "\nUSART2.c_r1: %p\n", USART2.c_r1); - usart_printf(&USART2, "NVIC intlinesnum: %d\n", - regget(NVIC.ict_r, nvic_intlinesnum)); - - int off = 1; - int last; - for(;;) { - int next = USART2.is_r & usart_rxne; - volatile int y = USART2.rd_r; - if (next) - USART2.td_r = y; - if (last != next) { - if (off) { - pin_on(pin3); - } else { - pin_off(pin3); - } - off = !off; - } - } - // usart_printf(&USART2, "Start Configuring Countdown!\n"); /* Set the countdown to start from 1,000,0000. */ diff --git a/02-usart/src/peri/dma.c b/02-usart/src/peri/dma.c new file mode 100644 index 0000000..ceae2e6 --- /dev/null +++ b/02-usart/src/peri/dma.c @@ -0,0 +1,312 @@ +#include "peri/dma.h" +#include "core/dma.h" +#include "core/usart.h" +#include "core/rcc.h" + + +/* Bitmask of DMA2 channels in use. */ +static uint8_t dma_inuse[2]; + +static inline dma_t* get_dma(int dma) +{ + if (dma) { + return &DMA2; + } else { + return &DMA1; + } +} + +static dma_t* get_raw_dma(dma_channel_t chan) +{ + return get_dma(chan.dma); +} + +static dma_channel_config_t* get_raw_channel_config(dma_channel_t chan) +{ + dma_t* dma = get_raw_dma(chan); + return &dma->channel_config[chan.chan]; +} + +static uint32_t get_periph_location(dma_peripheral_t operipheral) +{ +#define CASE(p, n) case p: return ptr2reg(n); + switch (operipheral) { + CASE(DMA1_PERIPH_USART1_RX, &USART1.rd_r) + CASE(DMA1_PERIPH_USART1_TX, &USART1.td_r) + CASE(DMA1_PERIPH_USART2_RX, &USART2.rd_r) + CASE(DMA1_PERIPH_USART2_TX, &USART2.td_r) + + default: + return 0; + }; +#undef CASE +} + +static dma_channel_t allocate_dma_channel( + dma_peripheral_t operipheral, int* modesel) +{ + dma_peripheral_t peripheral = operipheral & 0xff; + int dmasel = peripheral >= DMA2_DMA1_SWITCH__; + if (dmasel) { + peripheral -= DMA2_DMA1_SWITCH__; + } + int chan = peripheral % DMA_N_CHANNELS; + + *modesel = peripheral / 7; + return (dma_channel_t) { + .dma = dmasel, + .chan = chan + }; +} + +/* + * Atomically reserves the DMA channel so other calls + * cannot erroneously reserve the same DMA channel. + * + * Returns 0 if this function was unable to reserve + * the channel. + */ +static int try_reserve_dma_channel( + dma_channel_t chan) +{ + int in_use = __sync_fetch_and_or( + &dma_inuse[chan.dma], 1 << chan.chan); + + return !(in_use & (1 << chan.chan)); +} + + // int in_use = __sync_fetch_and_or(&dma_inuse[dmasel], 1 << chan); +void release_dma_channel(dma_channel_t chan) +{ + dma_channel_config_t* config = get_raw_channel_config(chan); + regset(config->cc_r, dma_cc_en, 0); /* Disable the register. */ + dma_inuse[chan.dma] &= ~(1 << chan.chan); /* Release the DMA. */ + + if (!dma_inuse[chan.dma]) { + /* Power-down the DMA if not in use. */ + if (chan.dma) { + regset(RCC.ahb1en_r, rcc_dma2en, 0); + } else { + regset(RCC.ahb1en_r, rcc_dma1en, 0); + } + } +} + +void configure_dma_channel( + dma_channel_t chan, + dma_peripheral_t operipheral, + dma_opts_t* opts, + dma_dir_t dir, + int selmode, + bool mem2mem, + int* error_out) +{ + if (chan.dma) { + regset(RCC.ahb1en_r, rcc_dma2en, 1); + } else { + regset(RCC.ahb1en_r, rcc_dma1en, 1); + } + + dma_t* dma = get_raw_dma(chan); + regset(dma->csel_r, 0xF << (4 * chan.chan), selmode); + dma_channel_config_t* config = + &dma->channel_config[chan.chan]; + + uint32_t reg = 0; + + regset(reg, dma_cc_dir, dir); + regset(reg, dma_cc_tcie, opts->transfer_complete_interrupt_enable); + regset(reg, dma_cc_htie, opts->half_transfer_interrupt_enable); + regset(reg, dma_cc_teie, opts->transfer_error_interrupt_enable); + regset(reg, dma_cc_circ, opts->circular_mode); + regset(reg, dma_cc_pinc, opts->peripheral_increment); + regset(reg, dma_cc_minc, opts->memory_increment); + regset(reg, dma_cc_psize, opts->peripheral_block_size); + regset(reg, dma_cc_msize, opts->memory_block_size); + regset(reg, dma_cc_pl, opts->priority); + regset(reg, dma_cc_mem2mem, mem2mem); + + config->cc_r = reg; + config->cpa_r = get_periph_location(operipheral); + + *error_out = 0; +} + +dma_mem2mem_channel_t select_dma_channel_mem2mem( + int channel, + dma_opts_t* opts, + int* error_out) +{ + +#define WRAP(c) ((dma_mem2mem_channel_t) { .c_ = c }) + // TODO this should probably be in a critical section. + dma_channel_t chan; + if (channel == -1) { + chan.dma = 1; + if ((dma_inuse[chan.dma] & 0x7F) == 0x7F) { + chan.dma = 0; + } + + if ((dma_inuse[chan.dma] & 0x7F) == 0x7F) { + *error_out = DMA_ERROR_CHANNEL_IN_USE; + return WRAP(DMA_CHAN_ERROR); + } + + uint8_t t = ~(dma_inuse[chan.dma] << 1); + chan.chan = 6 - (__builtin_clz(t) - 24); + } else { + if (channel < 7) { + chan.dma = 0; + chan.chan = channel; + } else { + chan.dma = 0; + chan.chan = channel - 7; + } + } + + if (!try_reserve_dma_channel(chan)) { + *error_out = DMA_ERROR_CHANNEL_IN_USE; + return WRAP(DMA_CHAN_ERROR); + } + + int ec = 0; + configure_dma_channel( + chan, + -1 /* No peripheral */, + opts, + READ_FROM_PERIPHERAL, + /* selmode = */ 0x8, + /* mem2mem = */ true, + &ec); + + if (ec) { + *error_out = ec; + return WRAP(DMA_CHAN_ERROR); + } + + *error_out = 0; + return WRAP(chan); +#undef WRAP +} + +dma_mem2p_channel_t select_dma_channel_mem2p( + dma_peripheral_t peripheral, + dma_opts_t* opts_in, + int* error_out) +{ +#define WRAP(c) ((dma_mem2p_channel_t) { .c_ = c }) + *error_out = 0; + + int modesel; + dma_channel_t ret = + allocate_dma_channel(peripheral, &modesel); + + if (!try_reserve_dma_channel(ret)) { + *error_out = DMA_ERROR_CHANNEL_IN_USE; + return WRAP(DMA_CHAN_ERROR); + } + + configure_dma_channel( + ret, + peripheral, + opts_in, + READ_FROM_MEMORY, + modesel, + /* mem2mem = */ false, + error_out); + + if (*error_out) { + return WRAP(DMA_CHAN_ERROR); + } + + *error_out = 0; + return WRAP(ret); +#undef WRAP +} + +dma_p2mem_channel_t select_dma_channel_p2mem( + dma_peripheral_t peripheral, + dma_opts_t* opts_in, + int* error_out) +{ +#define WRAP(c) ((dma_p2mem_channel_t) { .c_ = c }) + *error_out = 0; + + int modesel; + dma_channel_t ret = + allocate_dma_channel(peripheral, &modesel); + + if (!try_reserve_dma_channel(ret)) { + *error_out = DMA_ERROR_CHANNEL_IN_USE; + return WRAP(DMA_CHAN_ERROR); + } + + configure_dma_channel( + ret, + peripheral, + opts_in, + READ_FROM_PERIPHERAL, + modesel, + /* mem2mem = */ false, + error_out); + + if (*error_out) { + return WRAP(DMA_CHAN_ERROR); + } + + *error_out = 0; + return WRAP(ret); +#undef WRAP +} + + +void dma_mem2p_initiate_transfer( + dma_mem2p_channel_t chan, const void* from_loc, uint16_t nblocks) +{ + dma_channel_config_t* config = get_raw_channel_config(chan.c_); + config->cma_r = ptr2reg(from_loc); + config->cndt_r = nblocks; + + regset(config->cc_r, dma_cc_en, 1); +} + +void dma_mem2mem_initiate_transfer( + dma_mem2mem_channel_t chan, + void* to_loc, + const void* from_loc, + uint16_t nblocks) +{ + dma_channel_config_t* config = get_raw_channel_config(chan.c_); + config->cma_r = ptr2reg(to_loc); + config->cpa_r = ptr2reg(from_loc); + config->cndt_r = nblocks; + + regset(config->cc_r, dma_cc_en, 1); +} + +void dma_p2mem_initiate_transfer( + dma_p2mem_channel_t chan, void* to_loc, uint16_t nblocks) +{ + dma_channel_config_t* config = get_raw_channel_config(chan.c_); + + config->cma_r = ptr2reg(to_loc); + config->cndt_r = nblocks; + + regset(config->cc_r, dma_cc_en, 1); +} + +interrupt_t dma_channel_get_interrupt(dma_channel_t chan) +{ + if (chan.dma == 0) { + return IRQ_DMA1_CHANNEL1_IRQ + chan.chan; + } else { + switch (chan.chan) { + case 5: + return IRQ_DMA1_CHANNEL6_IRQ; + case 6: + return IRQ_DMA1_CHANNEL7_IRQ; + default: + return IRQ_DMA2_CHANNEL1_IRQ + chan.chan; + } + } +} diff --git a/02-usart/test_harness/fake_env.c b/02-usart/test_harness/fake_env.c index d1dba35..43a6caa 100644 --- a/02-usart/test_harness/fake_env.c +++ b/02-usart/test_harness/fake_env.c @@ -14,6 +14,9 @@ return fake_##name; \ } +/* Reset and clock control. */ +DEFINE_MEMORY_SEGMENT(rcc, 0x40021000, 0x400210A0) + /* Peripheral buses */ DEFINE_MEMORY_SEGMENT(apb1, 0x40000000, 0x40010000) DEFINE_MEMORY_SEGMENT(apb2, 0x40010000, 0x40020000) diff --git a/02-usart/test_harness/fake_env.h b/02-usart/test_harness/fake_env.h index 3cc6310..787abf5 100644 --- a/02-usart/test_harness/fake_env.h +++ b/02-usart/test_harness/fake_env.h @@ -11,5 +11,6 @@ void* load_fake_sram1__(); void* load_fake_sram2__(); void* load_fake_scb__(); void* load_fake_nvic__(); +void* load_fake_rcc__(); #endif /* FAKE_ENV_H_ */ diff --git a/02-usart/test_harness/test_harness.c b/02-usart/test_harness/test_harness.c index 3a6d10a..87606ee 100644 --- a/02-usart/test_harness/test_harness.c +++ b/02-usart/test_harness/test_harness.c @@ -26,7 +26,58 @@ test_t* iter = &__start_tests; static int execute_test(test_t* test); -int main() { +void test_printll(size_t sz, long long v1, long long v2) +{ + fprintf(stderr, "%lld == %lld\n", v1, v2); +} +void test_printul(size_t sz, unsigned long v1, unsigned long v2) +{ + fprintf(stderr, "%lu == %lu\n", v1, v2); +} +void test_printd(size_t sz, int v1, int v2) +{ + fprintf(stderr, "%d == %d\n", v1, v2); +} +void test_printl(size_t sz, long v1, long v2) +{ + fprintf(stderr, "%lu == %lu\n", v1, v2); +} +void test_printui(size_t sz, unsigned int v1, unsigned int v2) +{ + fprintf(stderr, "%u == %u\n", v1, v2); +} +void test_prints(size_t sz, short v1, short v2) +{ + fprintf(stderr, "%hu == %hu\n", v1, v2); +} +void test_printus(size_t sz, unsigned short v1, unsigned short v2) +{ + fprintf(stderr, "%hu == %hu\n", v1, v2); +} +void test_printc(size_t sz, char v1, char v2) +{ + fprintf(stderr, "'%c' == '%c'\n", v1, v2); +} +void test_printf(size_t sz, double v1, double v2) +{ + fprintf(stderr, "%f == %f\n", v1, v2); +} +void test_printp(size_t sz, void* v1, void* v2) +{ + fprintf(stderr, "%p == %p\n", v1, v2); +} +void test_printuc( + size_t sz, unsigned char v1, unsigned char v2) +{ + fprintf(stderr, "%02x == %02x\n", (int) v1, (int) v2); +} + +int do_fork = 1; +int main(int argc, char** argv) { + if (argc > 1 && strcmp(argv[1], "--nofork") == 0) { + do_fork = 0; + } + for( ; iter < &__stop_tests; ++ iter) { if (iter->fn_ptr != NULL) { execute_test(iter); @@ -44,12 +95,23 @@ static int execute_test(test_t* test) { char fullname[512]; int status; - int ec; + int ec = 0; pid_t pid; snprintf( fullname, sizeof(fullname), "%s::%s", iter->test_suite, iter->test_name); + if (!do_fork) { + if ((ec = setjmp(jmpbuf)) == 0) { + test->fn_ptr(); + printf("%s " GREEN "[PASS]" RESET "\n", fullname); + return 0; + } else { + printf("%s " RED "[FAIL] %d" RESET "\n", fullname, ec); + return ec; + } + } + if (!(pid = fork())) { // child diff --git a/02-usart/test_harness/test_harness.h b/02-usart/test_harness/test_harness.h index 74f3faa..b236cba 100644 --- a/02-usart/test_harness/test_harness.h +++ b/02-usart/test_harness/test_harness.h @@ -19,19 +19,31 @@ typedef struct { #define GENPR(fmt, v1, v2) \ fprintf(stderr, fmt "\n", v1, v2) +void test_printll(size_t sz, long long v1, long long v2); +void test_printul(size_t sz, unsigned long v1, unsigned long v2); +void test_printd(size_t sz, int v1, int v2); +void test_printl(size_t sz, long v1, long v2); +void test_printui(size_t sz, unsigned int v1, unsigned int v2); +void test_prints(size_t sz, short v1, short v2); +void test_printus(size_t sz, unsigned short v1, unsigned short v2); +void test_printc(size_t sz, char v1, char v2); +void test_printf(size_t sz, double v1, double v2); +void test_printp(size_t sz, void* v1, void* v2); +void test_printuc(size_t sz, unsigned char v1, unsigned char v2); + #define FORMAT_STRING(v1, v2) \ - fprintf( \ - stderr, \ - _Generic((v1), long: "%ld == %ld\n", \ - unsigned long: "%lu == %lu\n", \ - int: "%d == %d\n", \ - unsigned int: "%u == %u\n", \ - short: "%hu == %hu\n", \ - unsigned short: "%hu == %hu\n", \ - char: "%c == %c\n", \ - double: "%f == %f\n", \ - default: "%p == %p\n"), \ - (v1), (v2)); \ + _Generic((v1), \ + long long: test_printll, \ + unsigned long: test_printul, \ + int: test_printd, \ + long: test_printl, \ + unsigned int: test_printui, \ + short: test_prints, \ + unsigned short: test_printus, \ + char: test_printc, \ + unsigned char: test_printuc, \ + double: test_printf, \ + default: test_printp)(sizeof(v1), v1, v2) \ #define TRY_PRINT_TYPE(v1, v2, type, fmt) \ else if (__builtin_types_compatible_p(typeof (v1), type)) { \ @@ -54,7 +66,7 @@ typedef struct { do { \ if ((x) != (y)) { \ fprintf(stderr, RED "ASSERT_EQ FAILED! " RESET "Not true that "); \ - FORMAT_STRING(x, y); \ + FORMAT_STRING((x), (y)); \ fprintf(stderr, " - " YELLOW "In expression ASSERT_EQ(" #x ", " #y ")\n"); \ fprintf(stderr, RESET " - " YELLOW "At " __FILE__ ":%d\n" RESET, __LINE__); \ test_harness_abort(1); \ diff --git a/02-usart/tests/test_dma.c b/02-usart/tests/test_dma.c index 27ec022..ce0c4ba 100644 --- a/02-usart/tests/test_dma.c +++ b/02-usart/tests/test_dma.c @@ -1,5 +1,8 @@ #include "test_harness.h" #include "core/dma.h" +#include "core/rcc.h" +#include "core/usart.h" +#include "peri/dma.h" #include <stdio.h> #include <stdlib.h> @@ -8,7 +11,7 @@ TEST(dma, smoke) { dma_t* dma = &DMA1; - memset(dma, sizeof(dma), 0); + memset(dma, 0, sizeof(dma_t)); regset(dma->is_r, dma_tcif1, 1); ASSERT_EQ(dma->is_r, 2); @@ -20,7 +23,7 @@ TEST(dma, smoke) TEST(dma, cc_regset) { dma_t* dma = &DMA1; - memset(dma, sizeof(dma), 0); + memset(dma, 0, sizeof(dma_t)); dma_channel_config_t* channel_config = &dma->channel_config[2]; regset(channel_config->cc_r, dma_cc_msize, DMA_SIZE_32_BITS); @@ -37,3 +40,143 @@ TEST(dma, correct_align) return 0; } + +TEST(dma, regset_pl) +{ + uint32_t reg = 0; + + regset(reg, dma_cc_pl, DMA_PRIORITY_LEVEL_MEDIUM); + + ASSERT_EQ(reg, (1 << 12)); + + ASSERT_EQ( + regget(reg, dma_cc_pl), + DMA_PRIORITY_LEVEL_MEDIUM); +} + +TEST(dma_peri, select_peripheral) +{ + dma_opts_t opts = DEFAULT_DMA_OPTS; + int ec; + + dma_mem2p_channel_t chan = + select_dma_channel_mem2p( + DMA1_PERIPH_USART2_TX, + &opts, + &ec); + + ASSERT_EQ(DMA1.channel_config[6].cpa_r, ptr2reg(&USART2.td_r)); + + ASSERT_EQ( + regget(DMA1.channel_config[6].cc_r, dma_cc_dir), + READ_FROM_MEMORY); + + ASSERT_EQ( + regget(DMA1.channel_config[6].cc_r, dma_cc_minc), + 1); + + ASSERT_EQ( + regget(DMA1.channel_config[6].cc_r, dma_cc_pl), + DMA_PRIORITY_LEVEL_MEDIUM); + + ASSERT_EQ(regget(DMA1.csel_r, dma_c7s), 0x2); + + ASSERT_EQ(regget(RCC.ahb1en_r, rcc_dma1en), 1); + + release_dma_channel(chan.c_); + + ASSERT_EQ(regget(RCC.ahb1en_r, rcc_dma1en), 0); +} + +TEST(dma_peri, unable_to_realloc) +{ + dma_opts_t opts = DEFAULT_DMA_OPTS; + + int ec = 0; + + dma_mem2p_channel_t chan = + select_dma_channel_mem2p( + DMA1_PERIPH_USART2_TX, + &opts, + &ec); + + ASSERT_EQ(ec, 0); + + select_dma_channel_p2mem( + DMA1_PERIPH_USART2_TX, + &opts, + &ec); + + ASSERT_EQ(ec, DMA_ERROR_CHANNEL_IN_USE); + + release_dma_channel(chan.c_); + + chan = select_dma_channel_mem2p( + DMA1_PERIPH_USART2_TX, + &opts, + &ec); + + ASSERT_EQ(ec, 0); + + release_dma_channel(chan.c_); +} + +TEST(dma_peri, select_mem2mem) +{ + int ec = 0; + dma_opts_t opts = DEFAULT_DMA_OPTS; + dma_mem2mem_channel_t chan = + select_dma_channel_mem2mem(-1, &opts, &ec); + + ASSERT_EQ(ec, 0); + + ASSERT_EQ(chan.c_.dma, 1); + ASSERT_EQ(chan.c_.chan, 6); + + dma_mem2mem_channel_t chan2 = + select_dma_channel_mem2mem(-1, &opts, &ec); + + ASSERT_EQ(ec, 0); + + ASSERT_EQ(chan2.c_.dma, 1); + ASSERT_EQ(chan2.c_.chan, 5); + + release_dma_channel(chan.c_); + + dma_mem2mem_channel_t chan3 = + select_dma_channel_mem2mem(-1, &opts, &ec); + + ASSERT_EQ(chan3.c_.dma, 1); + ASSERT_EQ(chan3.c_.chan, 6); + + release_dma_channel(chan2.c_); + release_dma_channel(chan3.c_); +} + +TEST(dma_peri, select_mem2mem_2) +{ + dma_opts_t opts = DEFAULT_DMA_OPTS; + dma_mem2mem_channel_t chans[14]; + int ec; + + for (int i = 0; i < 14; ++ i) { + chans[i] = select_dma_channel_mem2mem( + -1, &opts, &ec); + + ASSERT_EQ(ec, 0); + } + + select_dma_channel_mem2mem(-1, &opts, &ec); + ASSERT_EQ(ec, DMA_ERROR_CHANNEL_IN_USE); + + for (int i = 0; i < 14; ++ i) { + if (i < 7) { + ASSERT_EQ(chans[i].c_.chan, 6 - i); + ASSERT_EQ(chans[i].c_.dma, 1); + } else { + ASSERT_EQ(chans[i].c_.chan, 6 - (i - 7)); + ASSERT_EQ(chans[i].c_.dma, 0); + } + release_dma_channel(chans[i].c_); + } +} |