Skip to content

Commit 1ef9900

Browse files
committed
[ISSUE-36]: UHCI / UDMA vs. UART FIFO feed performance comparison.
1 parent 3dd9317 commit 1ef9900

8 files changed

Lines changed: 320 additions & 3 deletions

File tree

configure.ac

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ AC_CONFIG_SUBDIRS([examples/0blink])
1919
AC_CONFIG_SUBDIRS([examples/0hello])
2020
AC_CONFIG_SUBDIRS([examples/0ledctrl])
2121
AC_CONFIG_SUBDIRS([examples/0uart])
22+
AC_CONFIG_SUBDIRS([examples/0udma])
2223
AC_CONFIG_SUBDIRS([examples/1rmtblink])
2324
AC_CONFIG_SUBDIRS([examples/1rmtdht])
2425
AC_CONFIG_SUBDIRS([examples/1rmtmorse])
@@ -38,6 +39,7 @@ AC_CONFIG_FILES([
3839
examples/0hello/Makefile
3940
examples/0ledctrl/Makefile
4041
examples/0uart/Makefile
42+
examples/0udma/Makefile
4143
examples/1rmtblink/Makefile
4244
examples/1rmtdht/Makefile
4345
examples/1rmtmorse/Makefile

examples/0udma/Makefile.am

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
include $(top_srcdir)/scripts/elf2bin.mk
2+
include $(top_srcdir)/ld/flags.mk
3+
4+
noinst_HEADERS = defines.h
5+
6+
AM_CFLAGS = -std=c11 -flto
7+
8+
if WITH_BINARIES
9+
AM_LDFLAGS += \
10+
-T $(top_srcdir)/ld/esp32.rom.ld \
11+
-T $(top_srcdir)/ld/esp32.rom.libgcc.ld \
12+
-T $(top_srcdir)/ld/esp32.rom.newlib-data.ld \
13+
-T $(top_srcdir)/ld/esp32.rom.newlib-locale.ld \
14+
-T $(top_srcdir)/ld/esp32.rom.newlib-nano.ld \
15+
-T $(top_srcdir)/ld/esp32.rom.newlib-time.ld \
16+
-T $(top_srcdir)/ld/esp32.rom.redefined.ld \
17+
-T $(top_srcdir)/ld/esp32.rom.syscalls.ld
18+
else
19+
AM_LDFLAGS += \
20+
-T $(top_srcdir)/ld/esp32.rom.ld \
21+
-T $(top_srcdir)/ld/esp32.rom.libgcc.ld \
22+
-T $(top_srcdir)/ld/esp32.rom.redefined.ld \
23+
-T $(top_srcdir)/ld/esp32.rom.syscalls.ld
24+
endif
25+
26+
AM_CPPFLAGS = -I$(top_srcdir)/src
27+
LDADD = $(top_builddir)/src/libesp32basic.a
28+
29+
bin_PROGRAMS = \
30+
uarttx.elf
31+
32+
if WITH_BINARIES
33+
CLEANFILES = \
34+
uarttx.bin
35+
endif
36+
37+
BUILT_SOURCES = $(CLEANFILES)

examples/0udma/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
### UART TX transfer performance comparison
2+
3+
In this example we compare UART FIFO feed speed to UDMA transfer.
4+
The application performs repeatedly the following phases:
5+
6+
* 1st phase - do UART FIFO feed;
7+
* 2nd phase - print UART FIFO feed duration;
8+
* 3rd phase - initiate UDMA TX transfer;
9+
* 4th phase - print UDMA transfer duration.
10+
11+
Between phases 3 and 4 we catch UHCI TX complete interrupt.
12+
After the last phase the length of the transferred message is increased (from 100 bytes up to 1000 bytes).
13+
14+
#### Hardware components
15+
16+
You have to access the `UART0` channel of the ESP32 somehow.
17+
Probably, you have flashed the binary to ESP32 over USB line
18+
and at the ESP32 end there is an *UART_over_USB* module (e.g., `cp210x converter`).
19+
So if you have an USB cable connecting the ESP32 module to your PC, that is fine.
20+
21+
#### Software requirements
22+
23+
On your PC, choose your favorite serial terminal tool.
24+
My choice is *miniterm*:
25+
```sh
26+
python -m serial.tools.miniterm --filter=direct /dev/ttyUSB0 115200 --rts 0
27+
```

examples/0udma/defines.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2025 SZIGETI János
3+
*
4+
* This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3.
5+
* See LICENSE or <https://www.gnu.org/licenses/> for full license details.
6+
*/
7+
#ifndef DEFINES_H
8+
#define DEFINES_H
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
// TIMINGS
15+
// const -- do not change this value
16+
#define APB_FREQ_HZ 80000000U // 80 MHz
17+
18+
// variables
19+
#define TIM0_0_DIVISOR 2U
20+
#define START_APP_CPU 0U
21+
#define SCHEDULE_FREQ_HZ 1000U // 10KHz
22+
23+
// derived invariants
24+
#define CLK_FREQ_HZ (APB_FREQ_HZ / TIM0_0_DIVISOR) // 40 MHz
25+
#define TICKS_PER_MS (CLK_FREQ_HZ / 1000U) // 40000
26+
#define TICKS_PER_US (CLK_FREQ_HZ / 1000000U) // 40
27+
28+
#define MS2TICKS(X) ((X) * TICKS_PER_MS)
29+
#define HZ2APBTICKS(X) (APB_FREQ_HZ / (X))
30+
31+
#ifdef __cplusplus
32+
}
33+
#endif
34+
35+
#endif /* DEFINES_H */
36+

examples/0udma/uarttx.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright 2024 SZIGETI János
3+
*
4+
* This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3.
5+
* See LICENSE or <https://www.gnu.org/licenses/> for full license details.
6+
*/
7+
#include <stdbool.h>
8+
#include <stdlib.h>
9+
#include <inttypes.h>
10+
11+
#include "esp_attr.h"
12+
#include "main.h"
13+
#include "defines.h"
14+
#include "dport.h"
15+
#include "romfunctions.h"
16+
#include "timg.h"
17+
#include "uart.h"
18+
#include "utils/uartutils.h"
19+
20+
// =================== Hard constants =================
21+
// #1: Timings
22+
#define UART_FREQ_HZ 115200U
23+
#define CYCLE_PERIOD_MS 1000U
24+
25+
// #2: Sizes
26+
#define TEST_PATTERN_LENGTH 1000U
27+
#define MSG_INC_STEP 100U
28+
29+
// #3: Channels
30+
#define UHCI_INT_CH 23U
31+
32+
// ============= Local types ===============
33+
typedef struct {
34+
uint32_t u12Size : 12;
35+
uint32_t u12Length : 12;
36+
uint32_t rsvd24: 6;
37+
uint32_t bEof : 1;
38+
uint32_t bOwner : 1;
39+
void *pcData;
40+
void *psNext;
41+
} UdmaDescriptor;
42+
43+
// ================ Local function declarations =================
44+
static void _pattern_init();
45+
static void _uart_init();
46+
static void _uart_cycle(uint64_t u64tckNow);
47+
static void _uhci_isr(void *pvParam);
48+
49+
// =================== Global constants ================
50+
const bool gbStartAppCpu = START_APP_CPU;
51+
const uint16_t gu16Tim00Divisor = TIM0_0_DIVISOR;
52+
const uint64_t gu64tckSchedulePeriod = (CLK_FREQ_HZ / SCHEDULE_FREQ_HZ);
53+
54+
// =================== Local constants ================
55+
static const TimerId gsTimer = {.eTimg = TIMG_0, .eTimer = TIMER0};
56+
static const EUartController geUart = UART_CTL0;
57+
static const EUdmaController geUdma = UDMA_CTL0;
58+
static const ECpu eIntCpu = CPU_PRO;
59+
60+
static UART_Type *gpsUART = geUart == UART_CTL0? &gsUART0 : geUart == UART_CTL1 ? &gsUART1 : &gsUART2;
61+
static UHCI_Type *gpsUHCI = geUdma == UDMA_CTL0? &gsUHCI0 : &gsUHCI1;
62+
63+
// ==================== Local Data ================
64+
static uint8_t gu8Phase = 0;
65+
66+
static uint64_t gu64TckUartTxStart;
67+
static uint64_t gu64TckUartTxStop;
68+
69+
static uint64_t gu64TckUdmaTxStart;
70+
static uint64_t gu64TckUdmaTxStop;
71+
static volatile uint64_t gu64TckUdmaTxDone;
72+
static volatile uint64_t gu64TckUdmaTxTotalEof;
73+
74+
static char gacTestPattern[TEST_PATTERN_LENGTH];
75+
static UdmaDescriptor gsUdmaDesc;
76+
77+
// Implementation
78+
79+
// ==================== Local Functions ================
80+
static void _pattern_init() {
81+
for (int i = 0; i < TEST_PATTERN_LENGTH; ++i) {
82+
gacTestPattern[i] = '0' + (i % 10);
83+
}
84+
}
85+
86+
static void _uart_init() {
87+
#define UART_CLKDIV_INT HZ2APBTICKS(UART_FREQ_HZ)
88+
#define UART_CLKDIV_REM (APB_FREQ_HZ - (UART_CLKDIV_INT * UART_FREQ_HZ))
89+
#define UART_CLKDIV_FRAG ((16U * UART_CLKDIV_REM) / UART_FREQ_HZ)
90+
// version A)
91+
gpsUART->CLKDIV.u20ClkDiv = UART_CLKDIV_INT;
92+
gpsUART->CLKDIV.u4ClkDivFrag = UART_CLKDIV_FRAG;
93+
// version B)
94+
// gpsUART->CLKDIV.raw = UART_CLKDIV_INT | (UART_CLKDIV_FRAG << 20); // this line results in shorter binary file
95+
#undef UART_CLKDIV_FRAG
96+
#undef UART_CLKDIV_REM
97+
#undef UART_CLKDIV_INT
98+
uart_init_udma(geUart, geUdma);
99+
gpsUHCI->INT_ENA = (1 << UHCI_INT_OUTTOTALEOF) | (1 << UHCI_INT_OUTDONE);
100+
101+
// register ISR and enable it
102+
RegAddr prDportIntMap = (eIntCpu == CPU_PRO ? &dport_regs()->PRO_UHCI0_INTR_MAP : &dport_regs()->APP_UHCI0_INTR_MAP);
103+
prDportIntMap += geUdma;
104+
105+
*prDportIntMap = UHCI_INT_CH;
106+
_xtos_set_interrupt_handler_arg(UHCI_INT_CH, _uhci_isr, 0);
107+
ets_isr_unmask(1 << UHCI_INT_CH);
108+
109+
}
110+
111+
static void _uart_cycle(uint64_t u64tckNow) {
112+
static uint64_t u64tckNext = 0;
113+
static uint32_t u32MsgLen = MSG_INC_STEP;
114+
115+
if (u64tckNext <= u64tckNow) {
116+
switch (gu8Phase) {
117+
case 0:
118+
uart_printf(gpsUART, "Sending message of %u bytes\r\n", u32MsgLen);
119+
break;
120+
case 1:
121+
gu64TckUartTxStart = timg_ticks(gsTimer);
122+
for (int i = 0; i < u32MsgLen; ++i) {
123+
gpsUART->FIFO = gacTestPattern[i];
124+
}
125+
gu64TckUartTxStop = timg_ticks(gsTimer);
126+
break;
127+
case 2:
128+
uart_printf(gpsUART, "\r\nUART TX duration: %u ns\r\n", (uint32_t)((gu64TckUartTxStop - gu64TckUartTxStart) / TICKS_PER_US));
129+
break;
130+
case 3:
131+
gsUdmaDesc = (UdmaDescriptor) {
132+
.bEof = true,
133+
.bOwner = true,
134+
.pcData = gacTestPattern,
135+
.psNext = NULL,
136+
.u12Length = u32MsgLen,
137+
.u12Size = TEST_PATTERN_LENGTH
138+
};
139+
gpsUHCI->INT_CLR = -1;
140+
gu64TckUdmaTxStart = timg_ticks(gsTimer);
141+
gpsUHCI->OUT_LINK = (((uint32_t)&gsUdmaDesc)&0xfffff) | (1 << 29);
142+
gu64TckUdmaTxStop = timg_ticks(gsTimer);
143+
break;
144+
case 4:
145+
uart_printf(gpsUART, "\r\nUDMA TX duration: %u (send), %u (done), %u (eof) ns)\r\n",
146+
(uint32_t)((gu64TckUdmaTxStop - gu64TckUdmaTxStart) / TICKS_PER_US),
147+
(uint32_t)((gu64TckUdmaTxDone - gu64TckUdmaTxStart) / TICKS_PER_US),
148+
(uint32_t)((gu64TckUdmaTxTotalEof - gu64TckUdmaTxStart) / TICKS_PER_US));
149+
break;
150+
}
151+
++gu8Phase;
152+
if (gu8Phase == 5) {
153+
gu8Phase = 0;
154+
u32MsgLen+=MSG_INC_STEP;
155+
if (TEST_PATTERN_LENGTH < u32MsgLen) {
156+
u32MsgLen = MSG_INC_STEP;
157+
}
158+
}
159+
u64tckNext += MS2TICKS(CYCLE_PERIOD_MS);
160+
}
161+
}
162+
163+
IRAM_ATTR static void _uhci_isr(void *pvParam) {
164+
bool bOutDoneEvent = gpsUHCI->INT_ST & (1 << UHCI_INT_OUTDONE);
165+
bool bOutTotalEofEvent = gpsUHCI->INT_ST & (1 << UHCI_INT_OUTTOTALEOF);
166+
if (bOutDoneEvent) {
167+
gu64TckUdmaTxDone = timg_ticks(gsTimer);
168+
gpsUHCI->INT_CLR = 1 << UHCI_INT_OUTDONE;
169+
}
170+
if (bOutTotalEofEvent) {
171+
gu64TckUdmaTxTotalEof = timg_ticks(gsTimer);
172+
gpsUHCI->INT_CLR = 1 << UHCI_INT_OUTTOTALEOF;
173+
}
174+
}
175+
176+
// ====================== Interface functions =========================
177+
178+
void prog_init_pro_pre() {
179+
_uart_init();
180+
_pattern_init();
181+
}
182+
183+
void prog_init_app() {
184+
}
185+
186+
void prog_init_pro_post() {
187+
}
188+
189+
void prog_cycle_app(uint64_t u64tckNow) {
190+
}
191+
192+
void prog_cycle_pro(uint64_t u64tckNow) {
193+
_uart_cycle(u64tckNow);
194+
}

examples/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
AUTOMAKE_OPTIONS =
2-
SUBDIRS=0blink 0button 0hello 0ledctrl 0uart 1rmtblink 1rmtdht 1rmtmorse 1rmtmusic 1rmttm1637 3prog1 1rmtws2812
2+
SUBDIRS=0blink 0button 0hello 0ledctrl 0uart 0udma 1rmtblink 1rmtdht 1rmtmorse 1rmtmusic 1rmttm1637 3prog1 1rmtws2812

src/romfunctions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ extern "C" {
1515
#include <stdint.h>
1616

1717
void ets_isr_unmask(uint32_t mask);
18-
void _xtos_set_interrupt_handler(int irq_number, void* function);
19-
void _xtos_set_interrupt_handler_arg(int irq_number, void* function, int argument);
18+
void *_xtos_set_interrupt_handler(int irq_number, void* function);
19+
void *_xtos_set_interrupt_handler_arg(int irq_number, void* function, int argument);
2020

2121
void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv);
2222
void gpio_matrix_in(uint32_t gpio, uint32_t signal_idx, bool inv);

src/uart.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@ extern "C" {
2424
UDMA_CTL1
2525
} EUdmaController;
2626

27+
typedef enum {
28+
UHCI_INT_RXSTART = 0,
29+
UHCI_INT_TXSTART,
30+
UHCI_INT_RXHUNG,
31+
UHCI_INT_TXHUNG,
32+
UHCI_INT_INDONE, // 4
33+
UHCI_INT_INSUCEOF,
34+
UHCI_INT_INERREOF,
35+
UHCI_INT_OUTDONE,
36+
UHCI_INT_OUTEOF, // 8
37+
UHCI_INT_INDSCRERR,
38+
UHCI_INT_OUTDSCRERR,
39+
UHCI_INT_INDSCREMPTY,
40+
UHCI_INT_OUTLINKEOFERR, // 12
41+
UHCI_INT_OUTTOTALEOF,
42+
UHCI_INT_SENDSREGQ,
43+
UHCI_INT_SENDAREGQ,
44+
UHCI_INT_DMAINFIFOFULLWM // 16
45+
} EUhciIntType; ///< Types of UHCI interrupt.
46+
47+
2748
// Based on:
2849
// https://github.com/espressif/esp-idf/blob/6b3da6b188/components/soc/esp32/include/soc/uart_reg.h
2950

0 commit comments

Comments
 (0)