Skip to content

Commit

Permalink
Implement systick
Browse files Browse the repository at this point in the history
  • Loading branch information
cpq committed Jun 26, 2023
1 parent b7f0be7 commit 009c9fa
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 37 deletions.
9 changes: 4 additions & 5 deletions templates/blinky/esp32-c3/Makefile
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
PROG ?= firmware
ESPUTIL ?= esputil
CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -pedantic
CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow
CFLAGS += -Wdouble-promotion -fno-common -Wconversion
CFLAGS += -march=rv32imc -mabi=ilp32
CFLAGS += -Os -ffunction-sections -fdata-sections -I.
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles -Wl,--gc-sections $(EXTRA_LINKFLAGS)
CWD ?= $(realpath $(CURDIR))
FLASH_ADDR ?= 0 # 2nd stage bootloader flash offset
DOCKER ?= docker run -it --rm -v $(CWD):$(CWD) -v $(MDK):$(MDK) -w $(CWD) mdashnet/riscv
TOOLCHAIN ?= $(DOCKER) riscv-none-elf
DOCKER ?= docker run -it --rm -v $(CWD):$(CWD) -w $(CWD) mdashnet/riscv
C ?= $(DOCKER) riscv-none-elf-gcc
SOURCES = startup.c main.c

build: $(PROG).bin

$(PROG).elf: $(SOURCES) hal.h link.ld Makefile
$(TOOLCHAIN)-gcc $(CFLAGS) $(CFLAGS_EXTRA) $(SOURCES) $(LDFLAGS) -o $@
# $(TOOLCHAIN)-size $@
$(C) $(CFLAGS) $(CFLAGS_EXTRA) $(SOURCES) $(LDFLAGS) -o $@

$(PROG).bin: $(PROG).elf
$(ESPUTIL) mkbin $(PROG).elf $@
Expand Down
117 changes: 103 additions & 14 deletions templates/blinky/esp32-c3/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#define LED_PIN 2 // Default LED pin
#endif

#ifndef BUTTON_PIN
#define BUTTON_PIN 9 // Default Button pin
#endif

#ifndef UART_DEBUG
#define UART_DEBUG C3_UART
#endif
Expand All @@ -22,7 +26,7 @@

#define BIT(x) ((uint32_t) 1U << (x))
#define REG(x) ((volatile uint32_t *) (x))
#define R(x) REG(x)[0]
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)

#define C3_SYSTEM 0x600c0000
#define C3_SENSITIVE 0x600c1000
Expand Down Expand Up @@ -67,6 +71,45 @@
#define C3_APB_SARADC 0x60040000
#define C3_AES_XTS 0x600CC000

#define CSR_WRITE(reg, val) ({ asm volatile("csrw " #reg ", %0" ::"rK"(val)); })
#define CSR_READ(reg) \
({ \
unsigned long v_; \
asm volatile("csrr %0, " #reg : "=r"(v_)); \
v_; \
})
#define CSR_SETBITS(reg, cm, sm) CSR_WRITE(reg, (CSR_READ(reg) & ~(cm)) | (sm))

struct sysreg { // System registers. 16.4 (incomplete)
volatile uint32_t CPU_PERI_CLK_EN, CPU_PERI_RST_EN, RESERVED0[2],
PERIPH_CLK_EN0, PERIPH_CLK_EN1, PERIPH_RST_EN0, PERIPH_RST_EN1;
};
#define SYSREG ((struct sysreg *) C3_SYSTEM)

struct systimer { // 10.6 (complete)
volatile uint32_t CONF, UNIT0_OP, UNIT1_OP, UNIT0_LOAD_HI, UNIT0_LOAD_LO,
UNIT1_LOAD_HI, UNIT1_LOAD_LO, TARGET0_HI, TARGET0_LO, TARGET1_HI,
TARGET1_LO, TARGET2_HI, TARGET2_LO, TARGET0_CONF, TARGET1_CONF,
TARGET2_CONF, UNIT0_VALUE_HI, UNIT0_VALUE_LO, UNIT1_VALUE_HI,
UNIT1_VALUE_LO, COMP0_LOAD, COMP1_LOAD, COMP2_LOAD, UNIT0_LOAD,
UNIT1_LOAD, INT_ENA, INT_RAW, INT_CLR, INT_ST, RESERVED0[34], DATE;
};
#define SYSTIMER ((struct systimer *) C3_SYSTIMER)

struct gpio { // 5.14 (incomplete)
volatile uint32_t BT_SELECT, OUT, OUT_W1TS, OUT_W1TC, RESERVED0[4], ENABLE,
ENABLE_W1TS, ENABLE_W1TC, RESERVED1[3], STRAP, IN, RESERVED2[1], STATUS,
STATUS_W1TS, STATUS_W1TC, RESERVED3[3], PCPU_INT, PCPU_NMI_INT,
// TODO(cpq): complete next
STATUS_NEXT, PIN[22], FUNC_IN[128], FUNC_OUT[22], DATE, CLOCK_GATE;
};
#define GPIO ((struct gpio *) C3_GPIO)

struct io_mux { // 5.14 (incomplete)
volatile uint32_t PIN_CTRL, IO[22];
};
#define IO_MUX ((struct io_mux *) C3_IO_MUX)

enum { GPIO_OUT_EN = 8, GPIO_OUT_FUNC = 341, GPIO_IN_FUNC = 85 };

// Perform `count` "NOP" operations
Expand All @@ -75,9 +118,9 @@ static inline void spin(volatile unsigned long count) {
}

static inline uint64_t systick(void) {
REG(C3_SYSTIMER)[1] = BIT(30); // TRM 10.5
SYSTIMER->UNIT0_OP = BIT(30); // TRM 10.5
spin(1);
return ((uint64_t) REG(C3_SYSTIMER)[16] << 32) | REG(C3_SYSTIMER)[17];
return ((uint64_t) SYSTIMER->UNIT0_VALUE_HI << 32) | SYSTIMER->UNIT0_VALUE_LO;
}

static inline uint64_t uptime_us(void) {
Expand Down Expand Up @@ -115,13 +158,17 @@ static inline void wifi_get_mac_addr(uint8_t mac[6]) {
mac[3] = (a >> 16) & 255, mac[4] = (a >> 8) & 255, mac[5] = a & 255;
}

static inline void set_irq_handler(void (*fn)(void)) {
CSR_WRITE(mtvec, (uintptr_t) fn);
}

static inline void soc_init(void) {
// Init clock. TRM 6.2.4.1
REG(C3_SYSTEM)[2] &= ~3U;
REG(C3_SYSTEM)[2] |= BIT(0) | BIT(2);
REG(C3_SYSTEM)[22] = BIT(19) | (40U << 12) | BIT(10);
// REG(C3_SYSTEM)[2] &= ~3U;
// REG(C3_SYSTEM)[2] |= BIT(0) | BIT(2);
// REG(C3_SYSTEM)[22] = BIT(19) | (40U << 12) | BIT(10);
// REG(C3_RTCCNTL)[47] = 0; // RTC_APB_FREQ_REG -> freq >> 12
((void (*)(int)) 0x40000588)(160); // ets_update_cpu_frequency(160)
//((void (*)(int)) 0x40000588)(160); // ets_update_cpu_frequency(160)
wdt_disable();

#if 0
Expand All @@ -135,8 +182,9 @@ static inline void soc_init(void) {
// API GPIO

static inline void gpio_output_enable(int pin, bool enable) {
REG(C3_GPIO)[GPIO_OUT_EN] &= ~BIT(pin);
REG(C3_GPIO)[GPIO_OUT_EN] |= (enable ? 1U : 0U) << pin;
GPIO->ENABLE &= ~BIT(pin);
GPIO->ENABLE |= (enable ? 1U : 0U) << pin;
//SETBITS(GPIO->ENABLE, BIT(pin), (enable ? BIT(pin) : 0U));
}

static inline void gpio_output(int pin) {
Expand All @@ -145,21 +193,21 @@ static inline void gpio_output(int pin) {
}

static inline void gpio_write(int pin, bool value) {
REG(C3_GPIO)[1] &= ~BIT(pin); // Clear first
REG(C3_GPIO)[1] |= (value ? 1U : 0U) << pin; // Then set
GPIO->OUT &= ~BIT(pin); // Clear first
GPIO->OUT |= (value ? 1U : 0U) << pin; // Then set
}

static inline void gpio_toggle(int pin) {
REG(C3_GPIO)[1] ^= BIT(pin);
GPIO->OUT ^= BIT(pin);
}

static inline void gpio_input(int pin) {
gpio_output_enable(pin, 0); // Disable output
REG(C3_IO_MUX)[1 + pin] = BIT(9) | BIT(8); // Enable pull-up
IO_MUX->IO[pin] = BIT(9) | BIT(8); // Enable pull-up
}

static inline bool gpio_read(int pin) {
return REG(C3_GPIO)[15] & BIT(pin) ? 1 : 0;
return GPIO->IN & BIT(pin) ? 1 : 0;
}

// API SPI
Expand Down Expand Up @@ -241,3 +289,44 @@ static inline bool timer_expired(volatile uint64_t *t, uint64_t prd,
*t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
return true; // Expired, return true
}

extern void uart_tx_one_char(uint8_t);
extern void esprv_intc_int_enable(uint32_t mask);
extern void esprv_intc_int_disable(uint32_t mask);
extern void esprv_intc_int_set_priority(uint32_t num, uint32_t prio);
extern void esprv_intc_int_set_type(uint32_t num, int type);

#define NIBBLE(c) ((c) < 10 ? (c) + '0' : (c) + 'W')
#define PUTCHAR(c) uart_tx_one_char(c)
static inline void hexdump(const void *buf, size_t len) {
const uint8_t *p = (const uint8_t *) buf;
char ascii[16];
size_t i, j, n = 0;
for (i = 0; i < len; i++) {
if ((i % 16) == 0) {
// Print buffered ascii chars
if (i > 0) {
PUTCHAR(' '), PUTCHAR(' ');
for (j = 0; j < sizeof(ascii); j++) PUTCHAR(ascii[j]);
PUTCHAR('\n'), n = 0;
}
// Print hex address, then \t
PUTCHAR(NIBBLE((i >> 12) & 15)), PUTCHAR(NIBBLE((i >> 8) & 15));
PUTCHAR(NIBBLE((i >> 4) & 15)), PUTCHAR('0');
PUTCHAR(' '), PUTCHAR(' '), PUTCHAR(' ');
}
PUTCHAR(NIBBLE(p[i] >> 4)), PUTCHAR(NIBBLE(p[i] & 15));
PUTCHAR(' '); // Space after hex number
if (p[i] >= ' ' && p[i] <= '~') {
ascii[n++] = (char) p[i]; // Printable
} else {
ascii[n++] = '.'; // Non-printable
}
}
if (n > 0) {
while (n < 16) PUTCHAR(' '), PUTCHAR(' '), PUTCHAR(' '), ascii[n++] = ' ';
PUTCHAR(' '), PUTCHAR(' ');
for (j = 0; j < sizeof(ascii); j++) PUTCHAR(ascii[j]);
}
PUTCHAR('\n');
}
16 changes: 16 additions & 0 deletions templates/blinky/esp32-c3/link.ld
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,20 @@ PROVIDE(uart_rx_one_char = 0x40000070);
PROVIDE(uart_rx_one_char_block = 0x40000074);
PROVIDE(uart_rx_readbuff = 0x40000078);

/*
PROVIDE(esprv_intc_int_set_priority = 0x400005e0);
PROVIDE(esprv_intc_int_set_threshold = 0x400005e4);
PROVIDE(esprv_intc_int_enable = 0x400005e8);
PROVIDE(esprv_intc_int_disable = 0x400005ec);
*/

esprv_intc_int_set_priority = 0x400005e0;
esprv_intc_int_set_threshold = 0x400005e4;
esprv_intc_int_enable = 0x400005e8;
esprv_intc_int_disable = 0x400005ec;
esprv_intc_int_set_type = 0x400005f0;
intr_matrix_set = 0x400005f4;
ets_intr_lock = 0x400005f8;
ets_intr_unlock = 0x400005fc;

PROVIDE(__divdi3 = 0x400007b4);
18 changes: 0 additions & 18 deletions templates/blinky/esp32-c3/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,10 @@ static void log_task(void) { // Print a log every LOG_PERIOD_MS
}
}

static inline uint64_t systick2(void) {
REG(C3_SYSTIMER)[2] = BIT(30); // TRM 10.5
spin(1);
// return ((uint64_t) REG(C3_SYSTIMER)[18]<< 32) | REG(C3_SYSTIMER + 0x4c);
return ((uint64_t) REG(C3_SYSTIMER)[18] << 32) | REG(C3_SYSTIMER)[19];
}

int main(void) {
gpio_output(LED_PIN);
uart_init(UART_DEBUG, 115200);

// STILL WORK IN PROGRESS!
// TRM 10.20. SYSTIMER_TARGET1_CONF -> ??
R(C3_SYSTIMER + 0x38) = BIT(30) | clock_sys_freq() / 1000;
R(C3_SYSTIMER + 0x64) = BIT(1); // SYSTIMER_TARGET1_INT_ENA

// TODO(cpq) - make systick interrupt work!
for (;;) {
printf("LED: %lu\n", (unsigned long) systick2());
delay_ms(500);
}

for (;;) {
led_task();
log_task();
Expand Down
66 changes: 66 additions & 0 deletions templates/blinky/esp32-c3/startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,76 @@ void *sbrk(int diff) {
return old;
}

// Mark it weak - allow user to override it
__attribute__((weak)) void SysTick_Handler(void) {
}

// Attribute interrupt makes this function to:
// 1. Return with mret instruction
// 2. Save/restore all used registers
__attribute__((interrupt)) void irq_handler(void) {
unsigned long mcause = CSR_READ(mcause), mepc = CSR_READ(mepc);
// printf("IRQ cause: %lx mepc: %lx mstatus: %lx\n", mcause, mepc,
// CSR_READ(mstatus));
if ((mcause & BIT(31))) {
// Interrupt
// uint32_t no = mcause << 1 >> 1;
// GPIO->PCPU_INT = GPIO->STATUS = 0U; // Clear GPIO IRQ
SYSTIMER->INT_CLR = 7U; // Clear systimer IRQ
SysTick_Handler();
} else {
// Exception
CSR_WRITE(mepc, mepc + 4);
}
}

// Vector table. Point all entries to the irq_handler()
__attribute__((aligned(256))) void irqtab(void) {
asm(".rept 32"); // 32 entries
asm("j irq_handler"); // Jump to irq_handler()
asm(".endr");
}

// ESP32C3 lets us bind peripheral interrupts to the CPU interrupts, 1..31
static int cpu_alloc_interrupt(uint8_t prio /* 1..15 */) {
static uint32_t allocated;
for (uint8_t no = 1; no < 31; no++) {
if (allocated & BIT(no)) continue; // Used, try the next one
allocated |= BIT(no); // Claim this one
REG(C3_INTERRUPT)[0x104 / 4] = BIT(no); // CPU_INT_ENA
REG(C3_INTERRUPT)[0x118 / 4 + no - 1] = prio; // CPU_INT_PRI_N
// REG(C3_INTERRUPT)[0x108 / 4] |= BIT(no); // Edge
return no;
}
return -1;
}

// Systimer is clocked by 16Mhz. Setup alarm and bind it to the CPU IRQ
static void systimer_init(void) {
SYSTIMER->TARGET0_CONF = BIT(30) | 16000; // Set period
SYSTIMER->COMP0_LOAD = BIT(0); // Reload period
SYSTIMER->CONF |= BIT(24); // Enable comparator 0
SYSTIMER->INT_ENA |= 7U; // Enable interrupts on all targets

int no = cpu_alloc_interrupt(1);
REG(C3_INTERRUPT)[0xfc / 4] |= BIT(5) | BIT(6) | BIT(7); // Enable CPU IRQ
REG(C3_INTERRUPT)[0xfc / 4] |= BIT(5); // Enable CPU IRQ
REG(C3_INTERRUPT)[0x94 / 4] = (uint8_t) no; // Map systimer IRQ to CPU
}

// GPIO IRQ
// REG(C3_INTERRUPT)[0x40 / 4] = no; // Map GPIO IRQ to CPU
// REG(C3_INTERRUPT)[0x44 / 4] = no; // Map GPIO IRQ to CPU
// REG(C3_INTERRUPT)[0xf8 / 4] |= BIT(16) | BIT(17); // Enable CPU IRQ
// REG(C3_GPIO)
//[0x74 / 4 + BTN_PIN] |= (3U << 7) | BIT(13); // Enable intr, any edge

void _reset(void) {
s_heap_start = s_brk = &_end, s_heap_end = &_eram;
for (char *p = &_sbss; p < &_ebss;) *p++ = '\0';
soc_init();
CSR_WRITE(mtvec, irqtab); // Route all interrupts to the irq_handler()
systimer_init();
SystemInit();
main();
for (;;) (void) 0;
Expand Down

0 comments on commit 009c9fa

Please sign in to comment.