aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--02-usart/Makefile.preamble2
-rw-r--r--02-usart/include/arch/arm/arch.h1
-rw-r--r--02-usart/include/arch/x86_64/arch.h2
-rw-r--r--02-usart/include/core/dma.h18
-rw-r--r--02-usart/include/core/isrs.inc10
-rw-r--r--02-usart/include/core/rcc.h3
-rw-r--r--02-usart/include/core/usart.h4
-rw-r--r--02-usart/include/peri/dma.h279
-rw-r--r--02-usart/src/main.c40
-rw-r--r--02-usart/src/peri/dma.c312
-rw-r--r--02-usart/test_harness/fake_env.c3
-rw-r--r--02-usart/test_harness/fake_env.h1
-rw-r--r--02-usart/test_harness/test_harness.c66
-rw-r--r--02-usart/test_harness/test_harness.h38
-rw-r--r--02-usart/tests/test_dma.c147
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_);
+ }
+}