diff --git a/.gitignore b/.gitignore index 7c759ade..8af9c4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ *.dis #*.exe -# Packages +# Packages ############ # Logs and Databases @@ -52,3 +52,5 @@ xtensa-esp32-elf/ xtensa-esp32-elf_psram/ backup.sh +.DS_Store +*.gch diff --git a/MicroPython_BUILD/components/internalfs_image/image/examples/pulse_count.py b/MicroPython_BUILD/components/internalfs_image/image/examples/pulse_count.py new file mode 100644 index 00000000..5687e822 --- /dev/null +++ b/MicroPython_BUILD/components/internalfs_image/image/examples/pulse_count.py @@ -0,0 +1,171 @@ + + + +""" +================================================================================ + +Pulse Counter using ESP32 ULP processor + + Uses ULP to count pulses on GPIO4 (which is TOUCH0/RTCIO10 in RTC domain) + +================================================================================ + +Includes debouncer taken from code as popularised by Kenneth A. Kuhn. + +This is an algorithm that debounces or removes random or spurious +transitions of a digital signal read as an input by a computer. This is +particularly applicable when the input is from a mechanical contact. + +The algorithm uses integration as opposed to edge logic +(differentiation), which makes it very robust in the presence of noise. + +An integrator is used to perform a time hysteresis so that the signal must +persistently be in a logical state (0 or 1) in order for the output to change +to that state. Random transitions of the input will not affect the output +except in the rare case where statistical clustering is longer than the +specified integration time. + +The following example illustrates how this algorithm works. "real signal" +represents the real intended signal with no noise. "corrupted" has significant +random transitions added to the real signal. "integrator" represents the +integration which is constrained to be between 0 and 3. "output" only makes a +transition when the integrator reaches either 0 or 3. + +Note that the output signal lags the input signal by the integration time but +is free of spurious transitions. + +real signal 0000111111110000000111111100000000011111111110000000000111111100000 + +corrupted 0100111011011001000011011010001001011100101111000100010111011100010 +integrator 0100123233233212100012123232101001012321212333210100010123233321010 +output 0000001111111111100000001111100000000111111111110000000001111111000 + + +-------------------------------------------------------------------------------- + +""" + +from machine import mem32 +import utime +import machine + +from esp32_ulp.__main__ import src_to_binary + +source = """\ +pulse_count: .long 0 # 16 bit count of pulses returned to main processor +debounce: .long 0 # maximum saturation value of integrator, from main +last_state: .long 0 # internal record of last debounced state of pin +integrator: .long 0 + + + .set RTC_GPIO_IN_REG, 0x3ff48424 # = DR_REG_RTCIO_BASEĀ + 0x24 + + /* Step 1: Update the integrator based on the input signal. Note that the + integrator follows the input, decreasing or increasing towards the limits as + determined by the input state (0 or 1). */ + +entry: move r3, integrator # get integrator address + reg_rd RTC_GPIO_IN_REG, 14, 24 # get all 16 ULP GPIOs into r0 + and r0, r0, 1 # isolate TOUCH0/RTCIO10/GPIO4 + jump pin_low, eq # if pin high + ld r0, r3, 0 # get integrator value + jumpr done, debounce, ge # if integrator < debounce max count + add r0, r0, 1 # integrator +=1 + st r0, r3, 0 + jump chk_state + +pin_low: + ld r0, r3, 0 # get integrator value + jumpr chk_state, 1, lt # if integrator >= 1 + sub r0, r0, 1 # integrator -=1 + st r0, r3, 0 + + /* Step 2: Update the output state based on the integrator. Note that the + output will only change state if the integrator has reached a limit, either + 0 or MAXIMUM. */ + +chk_state: + move r2, last_state # get last qualified state of pin + ld r0, r2, 0 + jumpr chk_debounce, 1, lt # if previous state was high + ld r0, r3, 0 # and + jumpr done, 1, ge # integrator < 1 + move r0, 0 # falling edge qualified + st r0, r2, 0 # so update last_state=0 + jump inc_pulse_count # update count on falling edge + +chk_debounce: # previous state was low + ld r0, r3, 0 # get integrator value + jumpr done, debounce, lt # if integrator >= debounce max + move r0,1 # rising edge qualified + st r0, r2, 0 # update last_state=1 + jump done # halt 'til next wakeup + +inc_pulse_count: # increment pulse count + move r3, pulse_count + ld r1, r3, 0 + add r1, r1, 1 + st r1, r3, 0 +done: halt # halt 'til next wakeup + +""" + + + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ULP_VARS = 4 # number of 32-bit vars used by ULP program + +load_addr = 0 +entry_addr = ULP_MEM_BASE + (ULP_VARS * 4) # entry address is offset by length of vars + +ulp_pulse_count = ULP_MEM_BASE + 0 +ulp_debounce = ULP_MEM_BASE + 4 + + +machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_UP) +pin21 = machine.Pin(21, machine.Pin.OUT) + + +print ("\nLoading ULP...\n") + +binary = src_to_binary(source) + +ulp = machine.ULP() +ulp.set_wakeup_period(0, 100) # use timer0, wakeup after ~100us +ulp.load_binary(load_addr, binary) + + +if machine.nvs_getint('pulse_count_nv') == None: + machine.nvs_setint('pulse_count_nv', 0) + +init_count = machine.nvs_getint('pulse_count_nv') + +pulse_count = init_count # init pulse counts +pulse_gen_count = init_count +mem32[ulp_pulse_count] = init_count + +mem32[ulp_debounce] = 3 # 3 sample debounce + +ulp.run(entry_addr) + + +print ("\n\nPulse Count Test: \n----------------- \n") + +while True: + pulse_countL = mem32[ulp_pulse_count] & ULP_DATA_MASK + if (pulse_count & 0xffff) > pulse_countL: + pulse_count += 0x10000 + pulse_count = (pulse_count & ~0xffff) + pulse_countL + machine.nvs_setint('pulse_count_nv', pulse_count) + + print('Pulses Out:', pulse_gen_count) + print('Pulses In :', pulse_count, '\n') + + for t in range (500): + pin21.value(1) + utime.sleep_ms(1) + pin21.value(0) + pulse_gen_count += 1 + utime.sleep_ms(1) diff --git a/MicroPython_BUILD/components/internalfs_image/image/examples/ulp_count.py b/MicroPython_BUILD/components/internalfs_image/image/examples/ulp_count.py new file mode 100644 index 00000000..fd4d5dd8 --- /dev/null +++ b/MicroPython_BUILD/components/internalfs_image/image/examples/ulp_count.py @@ -0,0 +1,47 @@ +""" +Very basic example showing data exchange main CPU <--> ULP coprocessor. + +To show that the ULP is doing something, it just increments the value . +It does that once per ulp timer wakeup (and then the ULP halts until it gets +waked up via the timer again). + +The timer is set to a rather long period, so you can watch the data value +incrementing (see loop at the end). +""" + +from machine import ULP +from machine import mem32 +import utime +import machine + +from esp32_ulp.__main__ import src_to_binary + +source = """\ +data: .long 0 + +entry: move r3, data # load address of data into r3 + ld r2, r3, 0 # load data contents ([r3+0]) into r2 + add r2, r2, 1 # increment r2 + st r2, r3, 0 # store r2 contents into data ([r3+0]) + halt # halt ULP co-processor (until next wake-up) +""" + + +print ("Wake Reason:",machine.wake_reason()) +binary = src_to_binary(source) + +load_addr, entry_addr = 0, 4 + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ulp = ULP() +ulp.set_wakeup_period(0, 1000000) # use timer0, wakeup after 1s +ulp.load_binary(load_addr, binary) + +mem32[ULP_MEM_BASE + load_addr] = 0x1000 +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)) + utime.sleep(1) diff --git a/MicroPython_BUILD/components/micropython/component.mk b/MicroPython_BUILD/components/micropython/component.mk index faae2b63..ded71916 100644 --- a/MicroPython_BUILD/components/micropython/component.mk +++ b/MicroPython_BUILD/components/micropython/component.mk @@ -181,6 +181,7 @@ SRC_C = $(addprefix esp32/,\ modymodem.c \ machine_hw_i2c.c \ machine_neopixel.c \ + machine_ulp.c \ machine_dht.c \ machine_ow.c \ ) diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_ulp.c b/MicroPython_BUILD/components/micropython/esp32/machine_ulp.c new file mode 100644 index 00000000..06d79c29 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/machine_ulp.c @@ -0,0 +1,100 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 "Eric Poulsen" + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "machine_ulp.h" + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "ulp.h" +#include "esp_err.h" + +typedef struct _machine_ulp_obj_t { + mp_obj_base_t base; +} machine_ulp_obj_t; + +const mp_obj_type_t machine_ulp_type; + +// singleton ULP object +STATIC const machine_ulp_obj_t machine_ulp_obj = {{&machine_ulp_type}}; + +STATIC mp_obj_t machine_ulp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // return constant object + return (mp_obj_t)&machine_ulp_obj; +} + +STATIC mp_obj_t machine_ulp_set_wakeup_period(mp_obj_t self_in, mp_obj_t period_index_in, mp_obj_t period_us_in) { + mp_uint_t period_index = mp_obj_get_int(period_index_in); + mp_uint_t period_us = mp_obj_get_int(period_us_in); + int _errno = ulp_set_wakeup_period(period_index, period_us); + if (_errno != ESP_OK) { + mp_raise_OSError(_errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_ulp_set_wakeup_period_obj, machine_ulp_set_wakeup_period); + +STATIC mp_obj_t machine_ulp_load_binary(mp_obj_t self_in, mp_obj_t load_addr_in, mp_obj_t program_binary_in) { + mp_uint_t load_addr = mp_obj_get_int(load_addr_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(program_binary_in, &bufinfo, MP_BUFFER_READ); + + int _errno = ulp_load_binary(load_addr, bufinfo.buf, bufinfo.len/sizeof(uint32_t)); + if (_errno != ESP_OK) { + mp_raise_OSError(_errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_ulp_load_binary_obj, machine_ulp_load_binary); + +STATIC mp_obj_t machine_ulp_run(mp_obj_t self_in, mp_obj_t entry_point_in) { + mp_uint_t entry_point = mp_obj_get_int(entry_point_in); + int _errno = ulp_run(entry_point/sizeof(uint32_t)); + if (_errno != ESP_OK) { + mp_raise_OSError(_errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_ulp_run_obj, machine_ulp_run); + +STATIC const mp_map_elem_t machine_ulp_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_set_wakeup_period), (mp_obj_t)&machine_ulp_set_wakeup_period_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_load_binary), (mp_obj_t)&machine_ulp_load_binary_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_run), (mp_obj_t)&machine_ulp_run_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_COPROC_RESERVE_MEM), MP_ROM_INT(CONFIG_ULP_COPROC_RESERVE_MEM) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_ulp_locals_dict, machine_ulp_locals_dict_table); + +const mp_obj_type_t machine_ulp_type = { + { &mp_type_type }, + .name = MP_QSTR_ULP, + .make_new = machine_ulp_make_new, + .locals_dict = (mp_obj_t)&machine_ulp_locals_dict, +}; diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_ulp.h b/MicroPython_BUILD/components/micropython/esp32/machine_ulp.h new file mode 100644 index 00000000..5ff0c924 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/machine_ulp.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 "Eric Poulsen" + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ESP32_MACINE_ULP_H +#define MICROPY_INCLUDED_ESP32_MACINE_ULP_H + +#endif diff --git a/MicroPython_BUILD/components/micropython/esp32/modmachine.c b/MicroPython_BUILD/components/micropython/esp32/modmachine.c index 1aeed635..15c0244b 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modmachine.c +++ b/MicroPython_BUILD/components/micropython/esp32/modmachine.c @@ -64,6 +64,7 @@ #include "extmod/machine_pulse.h" #include "extmod/vfs_native.h" #include "modmachine.h" +#include "machine_ulp.h" #include "mpsleep.h" #include "machine_rtc.h" #include "uart.h" @@ -1013,6 +1014,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&machine_ulp_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hw_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, diff --git a/MicroPython_BUILD/components/micropython/esp32/modmachine.h b/MicroPython_BUILD/components/micropython/esp32/modmachine.h index d1928b4d..9f38ce6e 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modmachine.h +++ b/MicroPython_BUILD/components/micropython/esp32/modmachine.h @@ -115,6 +115,7 @@ extern const mp_obj_type_t machine_pin_type; extern const mp_obj_type_t machine_touchpad_type; extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_dac_type; +extern const mp_obj_type_t machine_ulp_type; extern const mp_obj_type_t machine_pwm_type; extern const mp_obj_type_t machine_hw_spi_type; extern const mp_obj_type_t machine_hw_i2c_type; diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/__init__.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/__main__.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/__main__.py new file mode 100644 index 00000000..584a3dd7 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/__main__.py @@ -0,0 +1,36 @@ +import sys + +from .util import garbage_collect + +from .assemble import Assembler +from .link import make_binary +garbage_collect('after import') + + +def src_to_binary(src): + assembler = Assembler() + assembler.assemble(src) + garbage_collect('before symbols export') + addrs_syms = assembler.symbols.export() + for addr, sym in addrs_syms: + print('%04d %s' % (addr, sym)) + + text, data, bss_len = assembler.fetch() + return make_binary(text, data, bss_len) + + +def main(fn): + with open(fn) as f: + src = f.read() + + binary = src_to_binary(src) + + if fn.endswith('.s') or fn.endswith('.S'): + fn = fn[:-2] + with open(fn + '.ulp', 'wb') as f: + f.write(binary) + + +if __name__ == '__main__': + main(sys.argv[1]) + diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/assemble.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/assemble.py new file mode 100644 index 00000000..d0b1ff2c --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/assemble.py @@ -0,0 +1,283 @@ +""" +ESP32 ULP Co-Processor Assembler +""" + +from . import opcodes +from .nocomment import remove_comments +from .util import garbage_collect + +TEXT, DATA, BSS = 'text', 'data', 'bss' + +REL, ABS = 0, 1 + + +class SymbolTable: + def __init__(self, symbols, bases): + self._symbols = symbols + self._bases = bases + self._pass = None + + def set_pass(self, _pass): + self._pass = _pass + + def set_bases(self, bases): + self._bases = bases + + def set_from(self, from_section, from_offset): + self._from_section, self._from_offset = from_section, from_offset + + def get_from(self): + return self._from_section, self._from_offset + + def set_sym(self, symbol, stype, section, value): + entry = (stype, section, value) + if symbol in self._symbols and entry != self._symbols[symbol]: + raise Exception('redefining symbol %s with different value %r -> %r.' % (label, self._symbols[symbol], entry)) + self._symbols[symbol] = entry + + def has_sym(self, symbol): + return symbol in self._symbols + + def get_sym(self, symbol): + try: + entry = self._symbols[symbol] + except KeyError: + if self._pass == 1: + entry = (REL, TEXT, 0) # for a dummy, this is good enough + else: + raise + return entry + + def dump(self): + for symbol, entry in self._symbols.items(): + print(symbol, entry) + + def export(self): + addrs_syms = [(self.resolve_absolute(entry), symbol) for symbol, entry in self._symbols.items()] + return sorted(addrs_syms) + + def to_abs_addr(self, section, offset): + try: + base = self._bases[section] + except KeyError: + if self._pass == 1: + base = 0 # for a dummy this is good enough + else: + raise + return base + offset + + def resolve_absolute(self, symbol): + if isinstance(symbol, str): + stype, section, value = self.get_sym(symbol) + elif isinstance(symbol, tuple): + stype, section, value = symbol + else: + raise TypeError + if stype == REL: + return self.to_abs_addr(section, value) + if stype == ABS: + return value + raise TypeError(stype) + + def resolve_relative(self, symbol): + if isinstance(symbol, str): + sym_type, sym_section, sym_value = self.get_sym(symbol) + elif isinstance(symbol, tuple): + sym_type, sym_section, sym_value = symbol + else: + raise TypeError + if sym_type == REL: + sym_addr = self.to_abs_addr(sym_section, sym_value) + elif sym_type == ABS: + sym_addr = sym_value + from_addr = self.to_abs_addr(self._from_section, self._from_offset) + return sym_addr - from_addr + + +class Assembler: + + def __init__(self, symbols=None, bases=None): + self.symbols = SymbolTable(symbols or {}, bases or {}) + opcodes.symbols = self.symbols # XXX dirty hack + + def init(self, a_pass): + self.a_pass = a_pass + self.symbols.set_pass(a_pass) + self.sections = dict(text=[], data=[]) + self.offsets = dict(text=0, data=0, bss=0) + self.section = TEXT + + def parse_line(self, line): + """ + parse one line of assembler into label, opcode, args. + comments already have been removed by pre-processing. + + a line looks like (label, opcode, args, comment are all optional): + + label: opcode arg1, arg2, ... + """ + if not line: + return + has_label = line[0] not in '\t ' + if has_label: + label_line = line.split(None, 1) + if len(label_line) == 2: + label, line = label_line + else: # 1 + label, line = label_line[0], None + label = label.rstrip(':') + else: + label, line = None, line.lstrip() + if line is None: + opcode, args = None, () + else: + opcode_args = line.split(None, 1) + if len(opcode_args) == 2: + opcode, args = opcode_args + args = tuple(arg.strip() for arg in args.split(',')) + else: # 1 + opcode, args = opcode_args[0], () + return label, opcode, args + + + def parse(self, lines): + parsed = [self.parse_line(line) for line in lines] + return [p for p in parsed if p is not None] + + + def append_section(self, value, expected_section=None): + s = self.section + if expected_section is not None and s is not expected_section: + raise TypeError('only allowed in %s section' % expected_section) + if s is BSS: + # just increase BSS size by value + self.offsets[s] += value + else: + self.sections[s].append(value) + self.offsets[s] += len(value) + + def finalize_sections(self): + # make sure all sections have a bytelength dividable by 4, + # thus having all sections aligned at 32bit-word boundaries. + for s in list(self.sections.keys()) + [BSS, ]: + offs = self.offsets[s] + mod = offs % 4 + if mod: + fill = int(0).to_bytes(4 - mod, 'little') + self.offsets[s] += len(fill) + if s is not BSS: + self.sections[s].append(fill) + + def compute_bases(self): + bases = {} + addr = 0 + # lay out sections in this order: + for s in [TEXT, DATA, BSS]: # TODO: more flexibility for custom sections + bases[s] = addr + addr += self.offsets[s] // 4 # 32bit word addresses + return bases + + def dump(self): + print("Symbols:") + self.symbols.dump() + print("%s section:" % TEXT) + for t in self.sections[TEXT]: + print("%08x" % int.from_bytes(t, 'little')) + print("size: %d" % self.offsets[TEXT]) + print("%s section:" % DATA) + for d in self.sections[DATA]: + print("%08x" % int.from_bytes(d, 'little')) + print("size: %d" % self.offsets[DATA]) + print("%s section:" % BSS) + print("size: %d" % self.offsets[BSS]) + + def fetch(self): + def get_bytes(section): + return b''.join(self.sections[section]) + + return get_bytes(TEXT), get_bytes(DATA), self.offsets[BSS] + + def d_text(self): + self.section = TEXT + + def d_data(self): + self.section = DATA + + def d_bss(self): + self.section = BSS + + def fill(self, section, amount, fill_byte): + if fill_byte is not None and section is BSS: + raise ValueError('fill in bss section not allowed') + if section is TEXT: # TODO: text section should be filled with NOPs + raise ValueError('fill/skip/align in text section not supported') + fill = int(fill_byte or 0).to_bytes(1, 'little') * amount + self.offsets[section] += len(fill) + if section is not BSS: + self.sections[section].append(fill) + + def d_skip(self, amount, fill=None): + amount = int(amount) + self.fill(self.section, amount, fill) + + d_space = d_skip + + def d_align(self, align=4, fill=None): + align = int(align) + offs = self.offsets[self.section] + mod = offs % align + if mod: + amount = align - mod + self.fill(self.section, amount, fill) + + def d_set(self, symbol, expr): + value = int(expr) # TODO: support more than just integers + self.symbols.set_sym(symbol, ABS, None, value) + + def append_data(self, wordlen, args): + data = [int(arg).to_bytes(wordlen, 'little') for arg in args] + self.append_section(b''.join(data)) + + def d_byte(self, *args): + self.append_data(1, args) + + def d_word(self, *args): + self.append_data(2, args) + + def d_long(self, *args): + self.append_data(4, args) + + def assembler_pass(self, lines): + for label, opcode, args in self.parse(lines): + self.symbols.set_from(self.section, self.offsets[self.section] // 4) + if label is not None: + self.symbols.set_sym(label, REL, *self.symbols.get_from()) + if opcode is not None: + if opcode[0] == '.': + # assembler directive + func = getattr(self, 'd_' + opcode[1:]) + if func is not None: + result = func(*args) + if result is not None: + self.append_section(result) + continue + else: + # machine instruction + func = getattr(opcodes, 'i_' + opcode, None) + if func is not None: + instruction = func(*args) + self.append_section(instruction.to_bytes(4, 'little'), TEXT) + continue + raise Exception('Unknown opcode or directive: %s' % opcode) + self.finalize_sections() + + def assemble(self, text): + lines = remove_comments(text) + self.init(1) # pass 1 is only to get the symbol table right + self.assembler_pass(lines) + self.symbols.set_bases(self.compute_bases()) + garbage_collect('before pass2') + self.init(2) # now we know all symbols and bases, do the real assembler pass, pass 2 + self.assembler_pass(lines) + garbage_collect('after pass2') + diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/link.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/link.py new file mode 100644 index 00000000..fd9332bb --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/link.py @@ -0,0 +1,26 @@ +from uctypes import struct, addressof, LITTLE_ENDIAN, UINT16, UINT32 + + +def make_binary(text, data, bss_size): + if not isinstance(text, bytes): + raise TypeError('text section must be binary bytes') + if not isinstance(data, bytes): + raise TypeError('data section must be binary bytes') + binary_header_struct_def = dict( + magic = 0 | UINT32, + text_offset = 4 | UINT16, + text_size = 6 | UINT16, + data_size = 8 | UINT16, + bss_size = 10 | UINT16, + ) + header = bytearray(12) + h = struct(addressof(header), binary_header_struct_def, LITTLE_ENDIAN) + # https://github.com/espressif/esp-idf/blob/master/components/ulp/ld/esp32.ulp.ld + # ULP program binary should have the following format (all values little-endian): + h.magic = 0x00706c75 # (4 bytes) + h.text_offset = 12 # offset of .text section from binary start (2 bytes) + h.text_size = len(text) # size of .text section (2 bytes) + h.data_size = len(data) # size of .data section (2 bytes) + h.bss_size = bss_size # size of .bss section (2 bytes) + return bytes(header) + text + data + diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/nocomment.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/nocomment.py new file mode 100644 index 00000000..2ac9e8d7 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/nocomment.py @@ -0,0 +1,119 @@ +def remove_comments(s): + """ + Remove comments of these styles: + + CHASH: # comment python style, up to: EOL + CSLASHSLASH: // comment C style, up to: EOL + CSLASHSTAR: /* comment C style (single/multi line), up to: */ + + Strings can be like 'strings' or "strings". + Any comment-starting chars within strings are not considered. + Escaping of (string-end) chars via backslash in strings is considered. + + Also, leading and trailing whitespace is removed (after comment removal). + Indented lines are re-indented afterwards with a single tab char. + + Line numbers stay as in input file because empty lines are kept. + + s: string with comments (can include newlines) + returns: list of text lines + """ + # note: micropython's ure module was not capable enough to process this: + # missing methods, re modes, recursion limit exceeded, ... + # simpler hacks also didn't seem powerful enough to address all the + # corner cases of CSLASHSTAR vs. *STR, so this state machine came to life: + SRC, CHASH, CSLASHSLASH, CSLASHSTAR, DSTR, SSTR = range(6) # states + + line = [] # collect chars of one line + lines = [] # collect result lines + + def finish_line(): + # assemble a line from characters, try to get rid of trailing and + # most of leading whitespace (keep/put one tab for indented lines). + nonlocal line + line = ''.join(line) + is_indented = line.startswith(' ') or line.startswith('\t') + line = line.strip() + if line and is_indented: + line = '\t' + line + lines.append(line) + line = [] + + state = SRC + i = 0 + length = len(s) + while i < length: + c = s[i] + cn = s[i + 1] if i + 1 < length else '\0' + if state == SRC: + if c == '#': # starting to-EOL comment + state = CHASH + i += 1 + elif c == '/': + if cn == '/': # starting to-EOL comment + state = CSLASHSLASH + i += 2 + elif cn == '*': # starting a /* comment + state = CSLASHSTAR + i += 2 + else: + i += 1 + line.append(c) + elif c == '"': + state = DSTR + i += 1 + line.append(c) + elif c == "'": + state = SSTR + i += 1 + line.append(c) + elif c == '\n': + i += 1 + finish_line() + else: + i += 1 + line.append(c) + elif state == CHASH or state == CSLASHSLASH: + if c != '\n': # comment runs until EOL + i += 1 + else: + state = SRC + i += 1 + finish_line() + elif state == CSLASHSTAR: + if c == '*' and cn == '/': # ending a comment */ + state = SRC + i += 2 + elif c == '\n': + i += 1 + finish_line() + else: + i += 1 + elif state == DSTR and c == '"' or state == SSTR and c == "'": # string end + state = SRC + i += 1 + line.append(c) + elif state == DSTR or state == SSTR: + i += 1 + line.append(c) + if c == '\\': # escaping backslash + i += 1 # do not look at char after the backslash + line.append(cn) + else: + raise Exception("state: %d c: %s cn: %s" % (state, c, cn)) + if line: + # no final \n triggered processing these chars yet, do it now + finish_line() + return lines + + +if __name__ == '__main__': + import sys + filename = sys.argv[1] + with open(filename, "r") as f: + text = f.read() + lines = remove_comments(text) + with open(filename + ".nocomments", "w") as f: + for line in lines: + f.write(line + '\n') + diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/opcodes.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/opcodes.py new file mode 100644 index 00000000..4e2ca042 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/opcodes.py @@ -0,0 +1,647 @@ +""" +ESP32 ULP Co-Processor Instructions +""" + +from ucollections import namedtuple +from uctypes import struct, addressof, LITTLE_ENDIAN, UINT32, BFUINT32, BF_POS, BF_LEN + +from .soc import * + +# XXX dirty hack: use a global for the symbol table +symbols = None + +# Opcodes, Sub-Opcodes, Modes, ... + +OPCODE_WR_REG = 1 +OPCODE_RD_REG = 2 + +RD_REG_PERIPH_RTC_CNTL = 0 +RD_REG_PERIPH_RTC_IO = 1 +RD_REG_PERIPH_SENS = 2 +RD_REG_PERIPH_RTC_I2C = 3 + +OPCODE_I2C = 3 + +OPCODE_DELAY = 4 + +OPCODE_ADC = 5 + +OPCODE_ST = 6 +SUB_OPCODE_ST = 4 + +OPCODE_ALU = 7 +SUB_OPCODE_ALU_REG = 0 +SUB_OPCODE_ALU_IMM = 1 +ALU_SEL_ADD = 0 +ALU_SEL_SUB = 1 +ALU_SEL_AND = 2 +ALU_SEL_OR = 3 +ALU_SEL_MOV = 4 +ALU_SEL_LSH = 5 +ALU_SEL_RSH = 6 +SUB_OPCODE_ALU_CNT = 2 +ALU_SEL_INC = 0 +ALU_SEL_DEC = 1 +ALU_SEL_RST = 2 + +OPCODE_BRANCH = 8 +SUB_OPCODE_BX = 0 +BX_JUMP_TYPE_DIRECT = 0 +BX_JUMP_TYPE_ZERO = 1 +BX_JUMP_TYPE_OVF = 2 +SUB_OPCODE_B = 1 +B_CMP_L = 0 +B_CMP_GE = 1 +SUB_OPCODE_BC = 2 +BC_CMP_LT = 0 +BC_CMP_GT = 1 +BC_CMP_EQ = 2 + +OPCODE_END = 9 +SUB_OPCODE_END = 0 +SUB_OPCODE_SLEEP = 1 + +OPCODE_TSENS = 10 + +OPCODE_HALT = 11 + +OPCODE_LD = 13 + + +def make_ins_struct_def(layout): + lines = layout.strip().splitlines() + pos = 0 # bitfield definitions start from lsb + struct_def = {} + for line in lines: + bitfield = line.split('#', 1)[0] # get rid of comment + name, width = bitfield.split(':', 1) + name = name.strip() + width = int(width.strip()) + struct_def[name] = BFUINT32 | pos << BF_POS | width << BF_LEN + pos += width + if pos != 32: + raise ValueError('make_ins: bit field widths must sum up to 32. [%s]' % layout) + struct_def['all'] = UINT32 + return struct_def + + +def make_ins(layout): + """ + transform textual instruction layout description into a ready-to-use uctypes struct + """ + struct_def = make_ins_struct_def(layout) + instruction = bytearray(4) + return struct(addressof(instruction), struct_def, LITTLE_ENDIAN) + + +# instruction structure definitions + +_wr_reg = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + data : 8 # 8 bits of data to write + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_WR_REG) +""") + + +_rd_reg = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_WR_REG) +""") + + +_i2c = make_ins(""" + sub_addr : 8 # address within I2C slave + data : 8 # Data to write (not used for read) + low : 3 # low bit + high : 3 # high bit + i2c_sel : 4 # select i2c slave via SENS_I2C_SLAVE_ADDRx + unused : 1 # Unused + rw : 1 # Write (1) or read (0) + opcode : 4 # Opcode (OPCODE_I2C) +""") + + +_delay = make_ins(""" + cycles : 16 # Number of cycles to sleep + unused : 12 # Unused + opcode : 4 # Opcode (OPCODE_DELAY) +""") + + +_tsens = make_ins(""" + dreg : 2 # Register where to store TSENS result + delay : 14 # Number of cycles needed to obtain a measurement + unused : 12 # Unused + opcode : 4 # Opcode (OPCODE_TSENS) +""") + + +_adc = make_ins(""" + dreg : 2 # Register where to store ADC result + mux : 4 # Select SARADC pad (mux + 1) + sar_sel : 1 # Select SARADC0 (0) or SARADC1 (1) + unused1 : 1 # Unused + cycles : 16 # TBD, cycles used for measurement + unused2 : 4 # Unused + opcode: 4 # Opcode (OPCODE_ADC) +""") + + +_st = make_ins(""" + sreg : 2 # Register which contains data to store + dreg : 2 # Register which contains address in RTC memory (expressed in words) + unused1 : 6 # Unused + offset : 11 # Offset to add to dreg + unused2 : 4 # Unused + sub_opcode : 3 # Sub opcode (SUB_OPCODE_ST) + opcode : 4 # Opcode (OPCODE_ST) +""") + + +_alu_reg = make_ins(""" + dreg : 2 # Destination register + sreg : 2 # Register with operand A + treg : 2 # Register with operand B + unused : 15 # Unused + sel : 4 # Operation to perform, one of ALU_SEL_xxx + sub_opcode : 3 # Sub opcode (SUB_OPCODE_ALU_REG) + opcode : 4 # Opcode (OPCODE_ALU) +""") + + +_alu_imm = make_ins(""" + dreg : 2 # Destination register + sreg : 2 # Register with operand A + imm : 16 # Immediate value of operand B + unused : 1 # Unused + sel : 4 # Operation to perform, one of ALU_SEL_xxx + sub_opcode : 3 # Sub opcode (SUB_OPCODE_ALU_IMM) + opcode : 4 # Opcode (OPCODE_ALU) +""") + + +_alu_cnt = make_ins(""" + unused1 : 4 # Unused + imm : 8 # Immediate value (to inc / dec stage counter) + unused2 : 9 # Unused + sel : 4 # Operation to perform, one of ALU_SEL_xxx + sub_opcode : 3 # Sub opcode (SUB_OPCODE_ALU_CNT) + opcode : 4 # Opcode (OPCODE_ALU) +""") + + +_bx = make_ins(""" + dreg : 2 # Register which contains target PC, expressed in words (used if .reg == 1) + addr : 11 # Target PC, expressed in words (used if .reg == 0) + unused : 8 # Unused + reg : 1 # Target PC in register (1) or immediate (0) + type : 3 # Jump condition (BX_JUMP_TYPE_xxx) + sub_opcode : 3 # Sub opcode (SUB_OPCODE_BX) + opcode : 4 # Opcode (OPCODE_BRANCH) +""") + + +_b = make_ins(""" + imm : 16 # Immediate value to compare against + cmp : 1 # Comparison to perform: B_CMP_L or B_CMP_GE + offset : 7 # Absolute value of target PC offset w.r.t. current PC, expressed in words + sign : 1 # Sign of target PC offset: 0: positive, 1: negative + sub_opcode : 3 # Sub opcode (SUB_OPCODE_B) + opcode : 4 # Opcode (OPCODE_BRANCH) +""") + + +_bc = make_ins(""" + imm : 8 # Immediate value to compare against + unused : 7 # Unused + cmp : 2 # Comparison to perform: BC_CMP_LT, GT or EQ + offset : 7 # Absolute value of target PC offset w.r.t. current PC, expressed in words + sign : 1 # Sign of target PC offset: 0: positive, 1: negative + sub_opcode : 3 # Sub opcode (SUB_OPCODE_BC) + opcode : 4 # Opcode (OPCODE_BRANCH) +""") + + +_end = make_ins(""" + wakeup : 1 # Set to 1 to wake up chip + unused : 24 # Unused + sub_opcode : 3 # Sub opcode (SUB_OPCODE_END) + opcode : 4 # Opcode (OPCODE_END) +""") + + +_sleep = make_ins(""" + cycle_sel : 4 # Select which one of SARADC_ULP_CP_SLEEP_CYCx_REG to get the sleep duration from + unused : 21 # Unused + sub_opcode : 3 # Sub opcode (SUB_OPCODE_SLEEP) + opcode : 4 # Opcode (OPCODE_END) +""") + + +_halt = make_ins(""" + unused : 28 # Unused + opcode : 4 # Opcode (OPCODE_HALT) +""") + + +_ld = make_ins(""" + dreg : 2 # Register where the data should be loaded to + sreg : 2 # Register which contains address in RTC memory (expressed in words) + unused1 : 6 # Unused + offset : 11 # Offset to add to sreg + unused2 : 7 # Unused + opcode : 4 # Opcode (OPCODE_LD) +""") + + +# assembler opcode definitions + +REG, IMM, COND, SYM = 0, 1, 2, 3 +ARG = namedtuple('ARG', ('type', 'value', 'raw')) + + +def arg_qualify(arg): + """ + look at arg and qualify its type: + REG(ister), IMM(ediate) value + + then convert arg into a int value, e.g. 'R1' -> 1 or '0x20' -> 32. + + return result as ARG namedtuple + """ + arg_lower = arg.lower() + if len(arg) == 2: + if arg_lower[0] == 'r' and arg[1] in '0123456789': + reg = int(arg[1]) + if 0 <= reg <= 3: + return ARG(REG, reg, arg) + raise ValueError('arg_qualify: valid registers are r0, r1, r2, r3. Given: %s' % arg) + if arg_lower in ['--', 'eq', 'ov', 'lt', 'gt', 'ge']: + return ARG(COND, arg_lower, arg) + try: + return ARG(IMM, int(arg), arg) + except ValueError: + pass + entry = symbols.get_sym(arg) + return ARG(SYM, entry, arg) + + +def get_reg(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == REG: + return arg.value + raise TypeError('wanted: register, got: %s' % arg.raw) + + +def get_imm(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == IMM: + return arg.value + if arg.type == SYM: + return symbols.resolve_absolute(arg.value) + raise TypeError('wanted: immediate, got: %s' % arg.raw) + + +get_abs = get_imm + + +def get_rel(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == IMM: + return arg.value + if arg.type == SYM: + return symbols.resolve_relative(arg.value) + raise TypeError('wanted: immediate, got: %s' % arg.raw) + + +def get_cond(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == COND: + return arg.value + raise TypeError('wanted: condition, got: %s' % arg.raw) + + +def _soc_reg_to_ulp_periph_sel(reg): + # Map SoC peripheral register to periph_sel field of RD_REG and WR_REG instructions. + ret = 3 + if reg < DR_REG_RTCCNTL_BASE: + raise ValueError("invalid register base") + elif reg < DR_REG_RTCIO_BASE: + ret = RD_REG_PERIPH_RTC_CNTL + elif reg < DR_REG_SENS_BASE: + ret = RD_REG_PERIPH_RTC_IO + elif reg < DR_REG_RTC_I2C_BASE: + ret = RD_REG_PERIPH_SENS + elif reg < DR_REG_IO_MUX_BASE: + ret = RD_REG_PERIPH_RTC_I2C + else: + raise ValueError("invalid register base") + return ret + + +def i_reg_wr(reg, high_bit, low_bit, val): + reg = get_imm(reg) + _wr_reg.addr = (reg & 0xff) >> 2 + _wr_reg.periph_sel = _soc_reg_to_ulp_periph_sel(reg) + _wr_reg.data = get_imm(val) + _wr_reg.low = get_imm(low_bit) + _wr_reg.high = get_imm(high_bit) + _wr_reg.opcode = OPCODE_WR_REG + return _wr_reg.all + + +def i_reg_rd(reg, high_bit, low_bit): + reg = get_imm(reg) + _rd_reg.addr = (reg & 0xff) >> 2 + _rd_reg.periph_sel = _soc_reg_to_ulp_periph_sel(reg) + _rd_reg.unused = 0 + _rd_reg.low = get_imm(low_bit) + _rd_reg.high = get_imm(high_bit) + _rd_reg.opcode = OPCODE_RD_REG + return _rd_reg.all + + +def i_i2c_rd(sub_addr, high_bit, low_bit, slave_sel): + _i2c.sub_addr = get_imm(sub_addr) + _i2c.data = 0 + _i2c.low = get_imm(low_bit) + _i2c.high = get_imm(high_bit) + _i2c.i2c_sel = get_imm(slave_sel) + _i2c.unused = 0 + _i2c.rw = 0 + _i2c.opcode = OPCODE_I2C + return _i2c.all + + +def i_i2c_wr(sub_addr, value, high_bit, low_bit, slave_sel): + _i2c.sub_addr = get_imm(sub_addr) + _i2c.data = get_imm(value) + _i2c.low = get_imm(low_bit) + _i2c.high = get_imm(high_bit) + _i2c.i2c_sel = get_imm(slave_sel) + _i2c.unused = 0 + _i2c.rw = 1 + _i2c.opcode = OPCODE_I2C + return _i2c.all + + +def i_nop(): + _delay.cycles = 0 + _delay.unused = 0 + _delay.opcode = OPCODE_DELAY + return _delay.all + + +def i_wait(cycles): + _delay.cycles = get_imm(cycles) + _delay.unused = 0 + _delay.opcode = OPCODE_DELAY + return _delay.all + + +def i_tsens(reg_dest, delay): + _tsens.dreg = get_reg(reg_dest) + _tsens.delay = get_imm(delay) + _tsens.unused = 0 + _tsens.opcode = OPCODE_TSENS + return _tsens.all + + +def i_adc(reg_dest, adc_idx, mux): + _adc.dreg = get_reg(reg_dest) + _adc.mux = get_imm(mux) + _adc.sar_sel = get_imm(adc_idx) + _adc.unused1 = 0 + _adc.cycles = 0 + _adc.unused2 = 0 + _adc.opcode = OPCODE_ADC + return _adc.all + + +def i_st(reg_val, reg_addr, offset): + _st.dreg = get_reg(reg_addr) + _st.sreg = get_reg(reg_val) + _st.unused1 = 0 + _st.offset = get_imm(offset) // 4 + _st.unused2 = 0 + _st.sub_opcode = SUB_OPCODE_ST + _st.opcode = OPCODE_ST + return _st.all + + +def i_halt(): + _halt.unused = 0 + _halt.opcode = OPCODE_HALT + return _halt.all + + +def i_ld(reg_dest, reg_addr, offset): + _ld.dreg = get_reg(reg_dest) + _ld.sreg = get_reg(reg_addr) + _ld.unused1 = 0 + _ld.offset = get_imm(offset) // 4 + _ld.unused2 = 0 + _ld.opcode = OPCODE_LD + return _ld.all + + +def i_move(reg_dest, reg_imm_src): + # this is the only ALU instruction with 2 args: move r0, r1 + dest = get_reg(reg_dest) + src = arg_qualify(reg_imm_src) + if src.type == REG: + _alu_reg.dreg = dest + _alu_reg.sreg = src.value + _alu_reg.treg = 1 # XXX undocumented, this is the value binutils-esp32 uses + _alu_reg.unused = 0 + _alu_reg.sel = ALU_SEL_MOV + _alu_reg.sub_opcode = SUB_OPCODE_ALU_REG + _alu_reg.opcode = OPCODE_ALU + return _alu_reg.all + if src.type == IMM or src.type == SYM: + _alu_imm.dreg = dest + _alu_imm.sreg = 0 + _alu_imm.imm = get_abs(src) + _alu_imm.unused = 0 + _alu_imm.sel = ALU_SEL_MOV + _alu_imm.sub_opcode = SUB_OPCODE_ALU_IMM + _alu_imm.opcode = OPCODE_ALU + return _alu_imm.all + raise TypeError('unsupported operand: %s' % src.raw) + + +def _alu3(reg_dest, reg_src1, reg_imm_src2, alu_sel): + """ + ALU instructions with 3 args, like e.g. add r1, r2, r3 + """ + dest = get_reg(reg_dest) + src1 = get_reg(reg_src1) + src2 = arg_qualify(reg_imm_src2) + if src2.type == REG: + _alu_reg.dreg = dest + _alu_reg.sreg = src1 + _alu_reg.treg = src2.value + _alu_reg.unused = 0 + _alu_reg.sel = alu_sel + _alu_reg.sub_opcode = SUB_OPCODE_ALU_REG + _alu_reg.opcode = OPCODE_ALU + return _alu_reg.all + if src2.type == IMM or src2.type == SYM: + _alu_imm.dreg = dest + _alu_imm.sreg = src1 + _alu_imm.imm = get_abs(src2) + _alu_imm.unused = 0 + _alu_imm.sel = alu_sel + _alu_imm.sub_opcode = SUB_OPCODE_ALU_IMM + _alu_imm.opcode = OPCODE_ALU + return _alu_imm.all + raise TypeError('unsupported operand: %s' % src2.raw) + + +def i_add(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_ADD) + + +def i_sub(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_SUB) + + +def i_and(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_AND) + + +def i_or(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_OR) + + +def i_lsh(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_LSH) + + +def i_rsh(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_RSH) + + +def _alu_stage(imm, alu_sel): + """ + Stage counter instructions with 1 arg: stage_inc / stage_dec + """ + imm = get_imm(imm) + _alu_cnt.unused1 = 0 + _alu_cnt.imm = imm + _alu_cnt.unused2 = 0 + _alu_cnt.sel = alu_sel + _alu_cnt.sub_opcode = SUB_OPCODE_ALU_CNT + _alu_cnt.opcode = OPCODE_ALU + return _alu_cnt.all + + +def i_stage_inc(imm): + return _alu_stage(imm, ALU_SEL_INC) + + +def i_stage_dec(imm): + return _alu_stage(imm, ALU_SEL_DEC) + + +def i_stage_rst(): + return _alu_stage('0', ALU_SEL_RST) + + +def i_wake(): + _end.wakeup = 1 + _end.unused = 0 + _end.sub_opcode = SUB_OPCODE_END + _end.opcode = OPCODE_END + return _end.all + + +def i_sleep(timer_idx): + _sleep.cycle_sel = get_imm(timer_idx) + _sleep.unused = 0 + _sleep.sub_opcode = SUB_OPCODE_SLEEP + _sleep.opcode = OPCODE_END + return _sleep.all + + +def i_jump(target, condition='--'): + target = arg_qualify(target) + condition = get_cond(condition) + if condition == 'eq': + jump_type = BX_JUMP_TYPE_ZERO + elif condition == 'ov': + jump_type = BX_JUMP_TYPE_OVF + elif condition == '--': # means unconditional + jump_type = BX_JUMP_TYPE_DIRECT + else: + raise ValueError("invalid flags condition") + if target.type == IMM or target.type == SYM: + _bx.dreg = 0 + _bx.addr = get_abs(target) + _bx.unused = 0 + _bx.reg = 0 + _bx.type = jump_type + _bx.sub_opcode = SUB_OPCODE_BX + _bx.opcode = OPCODE_BRANCH + return _bx.all + if target.type == REG: + _bx.dreg = target.value + _bx.addr = 0 + _bx.unused = 0 + _bx.reg = 1 + _bx.type = jump_type + _bx.sub_opcode = SUB_OPCODE_BX + _bx.opcode = OPCODE_BRANCH + return _bx.all + raise TypeError('unsupported operand: %s' % target.raw) + + +def i_jumpr(offset, threshold, condition): + offset = get_rel(offset) + threshold = get_imm(threshold) + condition = get_cond(condition) + if condition == 'lt': + cmp_op = B_CMP_L + elif condition == 'ge': + cmp_op = B_CMP_GE + else: + raise ValueError("invalid comparison condition") + _b.imm = threshold + _b.cmp = cmp_op + _b.offset = abs(offset) + _b.sign = 0 if offset >= 0 else 1 + _b.sub_opcode = SUB_OPCODE_B + _b.opcode = OPCODE_BRANCH + return _b.all + + +def i_jumps(offset, threshold, condition): + offset = get_rel(offset) + threshold = get_imm(threshold) + condition = get_cond(condition) + if condition == 'lt': + cmp_op = BC_CMP_LT + elif condition == 'gt': + cmp_op = BC_CMP_GT + elif condition == 'eq': + cmp_op = BC_CMP_EQ + else: + raise ValueError("invalid comparison condition") + _bc.imm = threshold + _bc.cmp = cmp_op + _bc.offset = abs(offset) + _bc.sign = 0 if offset >= 0 else 1 + _bc.sub_opcode = SUB_OPCODE_BC + _bc.opcode = OPCODE_BRANCH + return _bc.all diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/soc.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/soc.py new file mode 100644 index 00000000..c6072e6c --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/soc.py @@ -0,0 +1,59 @@ +""" +Address / Register definitions for the ESP32 SoC +""" + +DR_REG_DPORT_BASE = 0x3ff00000 +DR_REG_AES_BASE = 0x3ff01000 +DR_REG_RSA_BASE = 0x3ff02000 +DR_REG_SHA_BASE = 0x3ff03000 +DR_REG_FLASH_MMU_TABLE_PRO = 0x3ff10000 +DR_REG_FLASH_MMU_TABLE_APP = 0x3ff12000 +DR_REG_DPORT_END = 0x3ff13FFC +DR_REG_UART_BASE = 0x3ff40000 +DR_REG_SPI1_BASE = 0x3ff42000 +DR_REG_SPI0_BASE = 0x3ff43000 +DR_REG_GPIO_BASE = 0x3ff44000 +DR_REG_GPIO_SD_BASE = 0x3ff44f00 +DR_REG_FE2_BASE = 0x3ff45000 +DR_REG_FE_BASE = 0x3ff46000 +DR_REG_FRC_TIMER_BASE = 0x3ff47000 +DR_REG_RTCCNTL_BASE = 0x3ff48000 +DR_REG_RTCIO_BASE = 0x3ff48400 +DR_REG_SENS_BASE = 0x3ff48800 +DR_REG_RTC_I2C_BASE = 0x3ff48C00 +DR_REG_IO_MUX_BASE = 0x3ff49000 +DR_REG_HINF_BASE = 0x3ff4B000 +DR_REG_UHCI1_BASE = 0x3ff4C000 +DR_REG_I2S_BASE = 0x3ff4F000 +DR_REG_UART1_BASE = 0x3ff50000 +DR_REG_BT_BASE = 0x3ff51000 +DR_REG_I2C_EXT_BASE = 0x3ff53000 +DR_REG_UHCI0_BASE = 0x3ff54000 +DR_REG_SLCHOST_BASE = 0x3ff55000 +DR_REG_RMT_BASE = 0x3ff56000 +DR_REG_PCNT_BASE = 0x3ff57000 +DR_REG_SLC_BASE = 0x3ff58000 +DR_REG_LEDC_BASE = 0x3ff59000 +DR_REG_EFUSE_BASE = 0x3ff5A000 +DR_REG_SPI_ENCRYPT_BASE = 0x3ff5B000 +DR_REG_NRX_BASE = 0x3ff5CC00 +DR_REG_BB_BASE = 0x3ff5D000 +DR_REG_PWM_BASE = 0x3ff5E000 +DR_REG_TIMERGROUP0_BASE = 0x3ff5F000 +DR_REG_TIMERGROUP1_BASE = 0x3ff60000 +DR_REG_RTCMEM0_BASE = 0x3ff61000 +DR_REG_RTCMEM1_BASE = 0x3ff62000 +DR_REG_RTCMEM2_BASE = 0x3ff63000 +DR_REG_SPI2_BASE = 0x3ff64000 +DR_REG_SPI3_BASE = 0x3ff65000 +DR_REG_SYSCON_BASE = 0x3ff66000 +DR_REG_APB_CTRL_BASE = 0x3ff66000 +DR_REG_I2C1_EXT_BASE = 0x3ff67000 +DR_REG_SDMMC_BASE = 0x3ff68000 +DR_REG_EMAC_BASE = 0x3ff69000 +DR_REG_PWM1_BASE = 0x3ff6C000 +DR_REG_I2S1_BASE = 0x3ff6D000 +DR_REG_UART2_BASE = 0x3ff6E000 +DR_REG_PWM2_BASE = 0x3ff6F000 +DR_REG_PWM3_BASE = 0x3ff70000 + diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/util.py b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/util.py new file mode 100644 index 00000000..c1844147 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/modules/esp32_ulp/util.py @@ -0,0 +1,11 @@ +DEBUG = False + +import gc + + +def garbage_collect(msg, verbose=DEBUG): + free_before = gc.mem_free() + gc.collect() + free_after = gc.mem_free() + if verbose: + print("%s: %d --gc--> %d bytes free" % (msg, free_before, free_after)) diff --git a/MicroPython_BUILD/components/micropython/esp32/ulp.h b/MicroPython_BUILD/components/micropython/esp32/ulp.h new file mode 100644 index 00000000..6960ac97 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/esp32/ulp.h @@ -0,0 +1,917 @@ +// Copyright 2016-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include +#include +#include "esp_err.h" +#include "soc/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ULP_FSM_PREPARE_SLEEP_CYCLES 2 /*!< Cycles spent by FSM preparing ULP for sleep */ +#define ULP_FSM_WAKEUP_SLEEP_CYCLES 2 /*!< Cycles spent by FSM waking up ULP from sleep */ + +/** + * @defgroup ulp_registers ULP coprocessor registers + * @{ + */ + + +#define R0 0 /*!< general purpose register 0 */ +#define R1 1 /*!< general purpose register 1 */ +#define R2 2 /*!< general purpose register 2 */ +#define R3 3 /*!< general purpose register 3 */ +/**@}*/ + +/** @defgroup ulp_opcodes ULP coprocessor opcodes, sub opcodes, and various modifiers/flags + * + * These definitions are not intended to be used directly. + * They are used in definitions of instructions later on. + * + * @{ + */ + +#define OPCODE_WR_REG 1 /*!< Instruction: write peripheral register (RTC_CNTL/RTC_IO/SARADC) (not implemented yet) */ + +#define OPCODE_RD_REG 2 /*!< Instruction: read peripheral register (RTC_CNTL/RTC_IO/SARADC) (not implemented yet) */ + +#define RD_REG_PERIPH_RTC_CNTL 0 /*!< Identifier of RTC_CNTL peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_RTC_IO 1 /*!< Identifier of RTC_IO peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_SENS 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_RTC_I2C 3 /*!< Identifier of RTC_I2C peripheral for RD_REG and WR_REG instructions */ + +#define OPCODE_I2C 3 /*!< Instruction: read/write I2C (not implemented yet) */ + +#define OPCODE_DELAY 4 /*!< Instruction: delay (nop) for a given number of cycles */ + +#define OPCODE_ADC 5 /*!< Instruction: SAR ADC measurement (not implemented yet) */ + +#define OPCODE_ST 6 /*!< Instruction: store indirect to RTC memory */ +#define SUB_OPCODE_ST 4 /*!< Store 32 bits, 16 MSBs contain PC, 16 LSBs contain value from source register */ + +#define OPCODE_ALU 7 /*!< Arithmetic instructions */ +#define SUB_OPCODE_ALU_REG 0 /*!< Arithmetic instruction, both source values are in register */ +#define SUB_OPCODE_ALU_IMM 1 /*!< Arithmetic instruction, one source value is an immediate */ +#define SUB_OPCODE_ALU_CNT 2 /*!< Arithmetic instruction between counter register and an immediate (not implemented yet)*/ +#define ALU_SEL_ADD 0 /*!< Addition */ +#define ALU_SEL_SUB 1 /*!< Subtraction */ +#define ALU_SEL_AND 2 /*!< Logical AND */ +#define ALU_SEL_OR 3 /*!< Logical OR */ +#define ALU_SEL_MOV 4 /*!< Copy value (immediate to destination register or source register to destination register */ +#define ALU_SEL_LSH 5 /*!< Shift left by given number of bits */ +#define ALU_SEL_RSH 6 /*!< Shift right by given number of bits */ + +#define OPCODE_BRANCH 8 /*!< Branch instructions */ +#define SUB_OPCODE_BX 0 /*!< Branch to absolute PC (immediate or in register) */ +#define BX_JUMP_TYPE_DIRECT 0 /*!< Unconditional jump */ +#define BX_JUMP_TYPE_ZERO 1 /*!< Branch if last ALU result is zero */ +#define BX_JUMP_TYPE_OVF 2 /*!< Branch if last ALU operation caused and overflow */ +#define SUB_OPCODE_B 1 /*!< Branch to a relative offset */ +#define B_CMP_L 0 /*!< Branch if R0 is less than an immediate */ +#define B_CMP_GE 1 /*!< Branch if R0 is greater than or equal to an immediate */ + +#define OPCODE_END 9 /*!< Stop executing the program */ +#define SUB_OPCODE_END 0 /*!< Stop executing the program and optionally wake up the chip */ +#define SUB_OPCODE_SLEEP 1 /*!< Stop executing the program and run it again after selected interval */ + +#define OPCODE_TSENS 10 /*!< Instruction: temperature sensor measurement (not implemented yet) */ + +#define OPCODE_HALT 11 /*!< Halt the coprocessor */ + +#define OPCODE_LD 13 /*!< Indirect load lower 16 bits from RTC memory */ + +#define OPCODE_MACRO 15 /*!< Not a real opcode. Used to identify labels and branches in the program */ +#define SUB_OPCODE_MACRO_LABEL 0 /*!< Label macro */ +#define SUB_OPCODE_MACRO_BRANCH 1 /*!< Branch macro */ +/**@}*/ + +/**@{*/ +#define ESP_ERR_ULP_BASE 0x1200 /*!< Offset for ULP-related error codes */ +#define ESP_ERR_ULP_SIZE_TOO_BIG (ESP_ERR_ULP_BASE + 1) /*!< Program doesn't fit into RTC memory reserved for the ULP */ +#define ESP_ERR_ULP_INVALID_LOAD_ADDR (ESP_ERR_ULP_BASE + 2) /*!< Load address is outside of RTC memory reserved for the ULP */ +#define ESP_ERR_ULP_DUPLICATE_LABEL (ESP_ERR_ULP_BASE + 3) /*!< More than one label with the same number was defined */ +#define ESP_ERR_ULP_UNDEFINED_LABEL (ESP_ERR_ULP_BASE + 4) /*!< Branch instructions references an undefined label */ +#define ESP_ERR_ULP_BRANCH_OUT_OF_RANGE (ESP_ERR_ULP_BASE + 5) /*!< Branch target is out of range of B instruction (try replacing with BX) */ +/**@}*/ + + +/** + * @brief Instruction format structure + * + * All ULP instructions are 32 bit long. + * This union contains field layouts used by all of the supported instructions. + * This union also includes a special "macro" instruction layout. + * This is not a real instruction which can be executed by the CPU. It acts + * as a token which is removed from the program by the + * ulp_process_macros_and_load function. + * + * These structures are not intended to be used directly. + * Preprocessor definitions provided below fill the fields of these structure with + * the right arguments. + */ +typedef union { + + struct { + uint32_t cycles : 16; /*!< Number of cycles to sleep */ + uint32_t unused : 12; /*!< Unused */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_DELAY) */ + } delay; /*!< Format of DELAY instruction */ + + struct { + uint32_t dreg : 2; /*!< Register which contains data to store */ + uint32_t sreg : 2; /*!< Register which contains address in RTC memory (expressed in words) */ + uint32_t unused1 : 6; /*!< Unused */ + uint32_t offset : 11; /*!< Offset to add to sreg */ + uint32_t unused2 : 4; /*!< Unused */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_ST) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_ST) */ + } st; /*!< Format of ST instruction */ + + struct { + uint32_t dreg : 2; /*!< Register where the data should be loaded to */ + uint32_t sreg : 2; /*!< Register which contains address in RTC memory (expressed in words) */ + uint32_t unused1 : 6; /*!< Unused */ + uint32_t offset : 11; /*!< Offset to add to sreg */ + uint32_t unused2 : 7; /*!< Unused */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_LD) */ + } ld; /*!< Format of LD instruction */ + + struct { + uint32_t unused : 28; /*!< Unused */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_HALT) */ + } halt; /*!< Format of HALT instruction */ + + struct { + uint32_t dreg : 2; /*!< Register which contains target PC, expressed in words (used if .reg == 1) */ + uint32_t addr : 11; /*!< Target PC, expressed in words (used if .reg == 0) */ + uint32_t unused : 8; /*!< Unused */ + uint32_t reg : 1; /*!< Target PC in register (1) or immediate (0) */ + uint32_t type : 3; /*!< Jump condition (BX_JUMP_TYPE_xxx) */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_BX) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_BRANCH) */ + } bx; /*!< Format of BRANCH instruction (absolute address) */ + + struct { + uint32_t imm : 16; /*!< Immediate value to compare against */ + uint32_t cmp : 1; /*!< Comparison to perform: B_CMP_L or B_CMP_GE */ + uint32_t offset : 7; /*!< Absolute value of target PC offset w.r.t. current PC, expressed in words */ + uint32_t sign : 1; /*!< Sign of target PC offset: 0: positive, 1: negative */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_B) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_BRANCH) */ + } b; /*!< Format of BRANCH instruction (relative address) */ + + struct { + uint32_t dreg : 2; /*!< Destination register */ + uint32_t sreg : 2; /*!< Register with operand A */ + uint32_t treg : 2; /*!< Register with operand B */ + uint32_t unused : 15; /*!< Unused */ + uint32_t sel : 4; /*!< Operation to perform, one of ALU_SEL_xxx */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_ALU_REG) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_ALU) */ + } alu_reg; /*!< Format of ALU instruction (both sources are registers) */ + + struct { + uint32_t dreg : 2; /*!< Destination register */ + uint32_t sreg : 2; /*!< Register with operand A */ + uint32_t imm : 16; /*!< Immediate value of operand B */ + uint32_t unused : 1; /*!< Unused */ + uint32_t sel : 4; /*!< Operation to perform, one of ALU_SEL_xxx */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_ALU_IMM) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_ALU) */ + } alu_imm; /*!< Format of ALU instruction (one source is an immediate) */ + + struct { + uint32_t addr : 8; /*!< Address within either RTC_CNTL, RTC_IO, or SARADC */ + uint32_t periph_sel : 2; /*!< Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) */ + uint32_t data : 8; /*!< 8 bits of data to write */ + uint32_t low : 5; /*!< Low bit */ + uint32_t high : 5; /*!< High bit */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_WR_REG) */ + } wr_reg; /*!< Format of WR_REG instruction */ + + struct { + uint32_t addr : 8; /*!< Address within either RTC_CNTL, RTC_IO, or SARADC */ + uint32_t periph_sel : 2; /*!< Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) */ + uint32_t unused : 8; /*!< Unused */ + uint32_t low : 5; /*!< Low bit */ + uint32_t high : 5; /*!< High bit */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_WR_REG) */ + } rd_reg; /*!< Format of RD_REG instruction */ + + struct { + uint32_t dreg : 2; /*!< Register where to store ADC result */ + uint32_t mux : 4; /*!< Select SARADC pad (mux + 1) */ + uint32_t sar_sel : 1; /*!< Select SARADC0 (0) or SARADC1 (1) */ + uint32_t unused1 : 1; /*!< Unused */ + uint32_t cycles : 16; /*!< TBD, cycles used for measurement */ + uint32_t unused2 : 4; /*!< Unused */ + uint32_t opcode: 4; /*!< Opcode (OPCODE_ADC) */ + } adc; /*!< Format of ADC instruction */ + + struct { + uint32_t dreg : 2; /*!< Register where to store temperature measurement result */ + uint32_t wait_delay: 14; /*!< Cycles to wait after measurement is done */ + uint32_t reserved: 12; /*!< Reserved, set to 0 */ + uint32_t opcode: 4; /*!< Opcode (OPCODE_TSENS) */ + } tsens; /*!< Format of TSENS instruction */ + + struct { + uint32_t i2c_addr : 8; /*!< I2C slave address */ + uint32_t data : 8; /*!< Data to read or write */ + uint32_t low_bits : 3; /*!< TBD */ + uint32_t high_bits : 3; /*!< TBD */ + uint32_t i2c_sel : 4; /*!< TBD, select reg_i2c_slave_address[7:0] */ + uint32_t unused : 1; /*!< Unused */ + uint32_t rw : 1; /*!< Write (1) or read (0) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_I2C) */ + } i2c; /*!< Format of I2C instruction */ + + struct { + uint32_t wakeup : 1; /*!< Set to 1 to wake up chip */ + uint32_t unused : 24; /*!< Unused */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_WAKEUP) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_END) */ + } end; /*!< Format of END instruction with wakeup */ + + struct { + uint32_t cycle_sel : 4; /*!< Select which one of SARADC_ULP_CP_SLEEP_CYCx_REG to get the sleep duration from */ + uint32_t unused : 21; /*!< Unused */ + uint32_t sub_opcode : 3; /*!< Sub opcode (SUB_OPCODE_SLEEP) */ + uint32_t opcode : 4; /*!< Opcode (OPCODE_END) */ + } sleep; /*!< Format of END instruction with sleep */ + + struct { + uint32_t label : 16; /*!< Label number */ + uint32_t unused : 8; /*!< Unused */ + uint32_t sub_opcode : 4; /*!< SUB_OPCODE_MACRO_LABEL or SUB_OPCODE_MACRO_BRANCH */ + uint32_t opcode: 4; /*!< Opcode (OPCODE_MACRO) */ + } macro; /*!< Format of tokens used by LABEL and BRANCH macros */ + +} ulp_insn_t; + +_Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should be 4 bytes"); + +/** + * Delay (nop) for a given number of cycles + */ +#define I_DELAY(cycles_) { .delay = {\ + .cycles = cycles_, \ + .unused = 0, \ + .opcode = OPCODE_DELAY } } + +/** + * Halt the coprocessor. + * + * This instruction halts the coprocessor, but keeps ULP timer active. + * As such, ULP program will be restarted again by timer. + * To stop the program and prevent the timer from restarting the program, + * use I_END(0) instruction. + */ +#define I_HALT() { .halt = {\ + .unused = 0, \ + .opcode = OPCODE_HALT } } + +/** + * Map SoC peripheral register to periph_sel field of RD_REG and WR_REG + * instructions. + * + * @param reg peripheral register in RTC_CNTL_, RTC_IO_, SENS_, RTC_I2C peripherals. + * @return periph_sel value for the peripheral to which this register belongs. + */ +static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { + uint32_t ret = 3; + if (reg < DR_REG_RTCCNTL_BASE) { + assert(0 && "invalid register base"); + } else if (reg < DR_REG_RTCIO_BASE) { + ret = RD_REG_PERIPH_RTC_CNTL; + } else if (reg < DR_REG_SENS_BASE) { + ret = RD_REG_PERIPH_RTC_IO; + } else if (reg < DR_REG_RTC_I2C_BASE){ + ret = RD_REG_PERIPH_SENS; + } else if (reg < DR_REG_IO_MUX_BASE){ + ret = RD_REG_PERIPH_RTC_I2C; + } else { + assert(0 && "invalid register base"); + } + return ret; +} + +/** + * Write literal value to a peripheral register + * + * reg[high_bit : low_bit] = val + * This instruction can access RTC_CNTL_, RTC_IO_, SENS_, and RTC_I2C peripheral registers. + */ +#define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ + .addr = (reg & 0xff) / sizeof(uint32_t), \ + .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ + .data = val, \ + .low = low_bit, \ + .high = high_bit, \ + .opcode = OPCODE_WR_REG } } + +/** + * Read from peripheral register into R0 + * + * R0 = reg[high_bit : low_bit] + * This instruction can access RTC_CNTL_, RTC_IO_, SENS_, and RTC_I2C peripheral registers. + */ +#define I_RD_REG(reg, low_bit, high_bit) {.rd_reg = {\ + .addr = (reg & 0xff) / sizeof(uint32_t), \ + .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ + .unused = 0, \ + .low = low_bit, \ + .high = high_bit, \ + .opcode = OPCODE_RD_REG } } + +/** + * Set or clear a bit in the peripheral register. + * + * Sets bit (1 << shift) of register reg to value val. + * This instruction can access RTC_CNTL_, RTC_IO_, SENS_, and RTC_I2C peripheral registers. + */ +#define I_WR_REG_BIT(reg, shift, val) I_WR_REG(reg, shift, shift, val) + +/** + * Wake the SoC from deep sleep. + * + * This instruction initiates wake up from deep sleep. + * Use esp_deep_sleep_enable_ulp_wakeup to enable deep sleep wakeup + * triggered by the ULP before going into deep sleep. + * Note that ULP program will still keep running until the I_HALT + * instruction, and it will still be restarted by timer at regular + * intervals, even when the SoC is woken up. + * + * To stop the ULP program, use I_HALT instruction. + * + * To disable the timer which start ULP program, use I_END() + * instruction. I_END instruction clears the + * RTC_CNTL_ULP_CP_SLP_TIMER_EN_S bit of RTC_CNTL_STATE0_REG + * register, which controls the ULP timer. + */ +#define I_WAKE() { .end = { \ + .wakeup = 1, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_END, \ + .opcode = OPCODE_END } } + +/** + * Stop ULP program timer. + * + * This is a convenience macro which disables the ULP program timer. + * Once this instruction is used, ULP program will not be restarted + * anymore until ulp_run function is called. + * + * ULP program will continue running after this instruction. To stop + * the currently running program, use I_HALT(). + */ +#define I_END() \ + I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0) +/** + * Select the time interval used to run ULP program. + * + * This instructions selects which of the SENS_SLEEP_CYCLES_Sx + * registers' value is used by the ULP program timer. + * When the ULP program stops at I_HALT instruction, ULP program + * timer start counting. When the counter reaches the value of + * the selected SENS_SLEEP_CYCLES_Sx register, ULP program + * start running again from the start address (passed to the ulp_run + * function). + * There are 5 SENS_SLEEP_CYCLES_Sx registers, so 0 <= timer_idx < 5. + * + * By default, SENS_SLEEP_CYCLES_S0 register is used by the ULP + * program timer. + */ +#define I_SLEEP_CYCLE_SEL(timer_idx) { .sleep = { \ + .cycle_sel = timer_idx, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_SLEEP, \ + .opcode = OPCODE_END } } + +/** + * Perform temperature sensor measurement and store it into reg_dest. + * + * Delay can be set between 1 and ((1 << 14) - 1). Higher values give + * higher measurement resolution. + */ +#define I_TSENS(reg_dest, delay) { .tsens = { \ + .dreg = reg_dest, \ + .wait_delay = delay, \ + .reserved = 0, \ + .opcode = OPCODE_TSENS } } + +/** + * Perform ADC measurement and store result in reg_dest. + * + * adc_idx selects ADC (0 or 1). + * pad_idx selects ADC pad (0 - 7). + */ +#define I_ADC(reg_dest, adc_idx, pad_idx) { .adc = {\ + .dreg = reg_dest, \ + .mux = pad_idx + 1, \ + .sar_sel = adc_idx, \ + .unused1 = 0, \ + .cycles = 0, \ + .unused2 = 0, \ + .opcode = OPCODE_ADC } } + +/** + * Store value from register reg_val into RTC memory. + * + * The value is written to an offset calculated by adding value of + * reg_addr register and offset_ field (this offset is expressed in 32-bit words). + * 32 bits written to RTC memory are built as follows: + * - bits [31:21] hold the PC of current instruction, expressed in 32-bit words + * - bits [20:16] = 5'b1 + * - bits [15:0] are assigned the contents of reg_val + * + * RTC_SLOW_MEM[addr + offset_] = { 5'b0, insn_PC[10:0], val[15:0] } + */ +#define I_ST(reg_val, reg_addr, offset_) { .st = { \ + .dreg = reg_val, \ + .sreg = reg_addr, \ + .unused1 = 0, \ + .offset = offset_, \ + .unused2 = 0, \ + .sub_opcode = SUB_OPCODE_ST, \ + .opcode = OPCODE_ST } } + + +/** + * Load value from RTC memory into reg_dest register. + * + * Loads 16 LSBs from RTC memory word given by the sum of value in reg_addr and + * value of offset_. + */ +#define I_LD(reg_dest, reg_addr, offset_) { .ld = { \ + .dreg = reg_dest, \ + .sreg = reg_addr, \ + .unused1 = 0, \ + .offset = offset_, \ + .unused2 = 0, \ + .opcode = OPCODE_LD } } + + +/** + * Branch relative if R0 less than immediate value. + * + * pc_offset is expressed in words, and can be from -127 to 127 + * imm_value is a 16-bit value to compare R0 against + */ +#define I_BL(pc_offset, imm_value) { .b = { \ + .imm = imm_value, \ + .cmp = B_CMP_L, \ + .offset = abs(pc_offset), \ + .sign = (pc_offset >= 0) ? 0 : 1, \ + .sub_opcode = SUB_OPCODE_B, \ + .opcode = OPCODE_BRANCH } } + +/** + * Branch relative if R0 greater or equal than immediate value. + * + * pc_offset is expressed in words, and can be from -127 to 127 + * imm_value is a 16-bit value to compare R0 against + */ +#define I_BGE(pc_offset, imm_value) { .b = { \ + .imm = imm_value, \ + .cmp = B_CMP_GE, \ + .offset = abs(pc_offset), \ + .sign = (pc_offset >= 0) ? 0 : 1, \ + .sub_opcode = SUB_OPCODE_B, \ + .opcode = OPCODE_BRANCH } } + +/** + * Unconditional branch to absolute PC, address in register. + * + * reg_pc is the register which contains address to jump to. + * Address is expressed in 32-bit words. + */ +#define I_BXR(reg_pc) { .bx = { \ + .dreg = reg_pc, \ + .addr = 0, \ + .unused = 0, \ + .reg = 1, \ + .type = BX_JUMP_TYPE_DIRECT, \ + .sub_opcode = SUB_OPCODE_BX, \ + .opcode = OPCODE_BRANCH } } + +/** + * Unconditional branch to absolute PC, immediate address. + * + * Address imm_pc is expressed in 32-bit words. + */ +#define I_BXI(imm_pc) { .bx = { \ + .dreg = 0, \ + .addr = imm_pc, \ + .unused = 0, \ + .reg = 0, \ + .type = BX_JUMP_TYPE_DIRECT, \ + .sub_opcode = SUB_OPCODE_BX, \ + .opcode = OPCODE_BRANCH } } + +/** + * Branch to absolute PC if ALU result is zero, address in register. + * + * reg_pc is the register which contains address to jump to. + * Address is expressed in 32-bit words. + */ +#define I_BXZR(reg_pc) { .bx = { \ + .dreg = reg_pc, \ + .addr = 0, \ + .unused = 0, \ + .reg = 1, \ + .type = BX_JUMP_TYPE_ZERO, \ + .sub_opcode = SUB_OPCODE_BX, \ + .opcode = OPCODE_BRANCH } } + +/** + * Branch to absolute PC if ALU result is zero, immediate address. + * + * Address imm_pc is expressed in 32-bit words. + */ +#define I_BXZI(imm_pc) { .bx = { \ + .dreg = 0, \ + .addr = imm_pc, \ + .unused = 0, \ + .reg = 0, \ + .type = BX_JUMP_TYPE_ZERO, \ + .sub_opcode = SUB_OPCODE_BX, \ + .opcode = OPCODE_BRANCH } } + +/** + * Branch to absolute PC if ALU overflow, address in register + * + * reg_pc is the register which contains address to jump to. + * Address is expressed in 32-bit words. + */ +#define I_BXFR(reg_pc) { .bx = { \ + .dreg = reg_pc, \ + .addr = 0, \ + .unused = 0, \ + .reg = 1, \ + .type = BX_JUMP_TYPE_OVF, \ + .sub_opcode = SUB_OPCODE_BX, \ + .opcode = OPCODE_BRANCH } } + +/** + * Branch to absolute PC if ALU overflow, immediate address + * + * Address imm_pc is expressed in 32-bit words. + */ +#define I_BXFI(imm_pc) { .bx = { \ + .dreg = 0, \ + .addr = imm_pc, \ + .unused = 0, \ + .reg = 0, \ + .type = BX_JUMP_TYPE_OVF, \ + .sub_opcode = SUB_OPCODE_BX, \ + .opcode = OPCODE_BRANCH } } + + +/** + * Addition: dest = src1 + src2 + */ +#define I_ADDR(reg_dest, reg_src1, reg_src2) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src1, \ + .treg = reg_src2, \ + .unused = 0, \ + .sel = ALU_SEL_ADD, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + +/** + * Subtraction: dest = src1 - src2 + */ +#define I_SUBR(reg_dest, reg_src1, reg_src2) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src1, \ + .treg = reg_src2, \ + .unused = 0, \ + .sel = ALU_SEL_SUB, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + +/** + * Logical AND: dest = src1 & src2 + */ +#define I_ANDR(reg_dest, reg_src1, reg_src2) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src1, \ + .treg = reg_src2, \ + .unused = 0, \ + .sel = ALU_SEL_AND, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + +/** + * Logical OR: dest = src1 | src2 + */ +#define I_ORR(reg_dest, reg_src1, reg_src2) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src1, \ + .treg = reg_src2, \ + .unused = 0, \ + .sel = ALU_SEL_OR, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + +/** + * Copy: dest = src + */ +#define I_MOVR(reg_dest, reg_src) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .treg = 0, \ + .unused = 0, \ + .sel = ALU_SEL_MOV, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + +/** + * Logical shift left: dest = src << shift + */ +#define I_LSHR(reg_dest, reg_src, reg_shift) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .treg = reg_shift, \ + .unused = 0, \ + .sel = ALU_SEL_LSH, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + + +/** + * Logical shift right: dest = src >> shift + */ +#define I_RSHR(reg_dest, reg_src, reg_shift) { .alu_reg = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .treg = reg_shift, \ + .unused = 0, \ + .sel = ALU_SEL_RSH, \ + .sub_opcode = SUB_OPCODE_ALU_REG, \ + .opcode = OPCODE_ALU } } + +/** + * Add register and an immediate value: dest = src1 + imm + */ +#define I_ADDI(reg_dest, reg_src, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_ADD, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + + +/** + * Subtract register and an immediate value: dest = src - imm + */ +#define I_SUBI(reg_dest, reg_src, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_SUB, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + +/** + * Logical AND register and an immediate value: dest = src & imm + */ +#define I_ANDI(reg_dest, reg_src, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_AND, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + +/** + * Logical OR register and an immediate value: dest = src | imm + */ +#define I_ORI(reg_dest, reg_src, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_OR, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + +/** + * Copy an immediate value into register: dest = imm + */ +#define I_MOVI(reg_dest, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = 0, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_MOV, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + +/** + * Logical shift left register value by an immediate: dest = src << imm + */ +#define I_LSHI(reg_dest, reg_src, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_LSH, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + + +/** + * Logical shift right register value by an immediate: dest = val >> imm + */ +#define I_RSHI(reg_dest, reg_src, imm_) { .alu_imm = { \ + .dreg = reg_dest, \ + .sreg = reg_src, \ + .imm = imm_, \ + .unused = 0, \ + .sel = ALU_SEL_RSH, \ + .sub_opcode = SUB_OPCODE_ALU_IMM, \ + .opcode = OPCODE_ALU } } + +/** + * Define a label with number label_num. + * + * This is a macro which doesn't generate a real instruction. + * The token generated by this macro is removed by ulp_process_macros_and_load + * function. Label defined using this macro can be used in branch macros defined + * below. + */ +#define M_LABEL(label_num) { .macro = { \ + .label = label_num, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_MACRO_LABEL, \ + .opcode = OPCODE_MACRO } } + +/** + * Token macro used by M_B and M_BX macros. Not to be used directly. + */ +#define M_BRANCH(label_num) { .macro = { \ + .label = label_num, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_MACRO_BRANCH, \ + .opcode = OPCODE_MACRO } } + +/** + * Macro: branch to label label_num if R0 is less than immediate value. + * + * This macro generates two ulp_insn_t values separated by a comma, and should + * be used when defining contents of ulp_insn_t arrays. First value is not a + * real instruction; it is a token which is removed by ulp_process_macros_and_load + * function. + */ +#define M_BL(label_num, imm_value) \ + M_BRANCH(label_num), \ + I_BL(0, imm_value) + +/** + * Macro: branch to label label_num if R0 is greater or equal than immediate value + * + * This macro generates two ulp_insn_t values separated by a comma, and should + * be used when defining contents of ulp_insn_t arrays. First value is not a + * real instruction; it is a token which is removed by ulp_process_macros_and_load + * function. + */ +#define M_BGE(label_num, imm_value) \ + M_BRANCH(label_num), \ + I_BGE(0, imm_value) + +/** + * Macro: unconditional branch to label + * + * This macro generates two ulp_insn_t values separated by a comma, and should + * be used when defining contents of ulp_insn_t arrays. First value is not a + * real instruction; it is a token which is removed by ulp_process_macros_and_load + * function. + */ +#define M_BX(label_num) \ + M_BRANCH(label_num), \ + I_BXI(0) + +/** + * Macro: branch to label if ALU result is zero + * + * This macro generates two ulp_insn_t values separated by a comma, and should + * be used when defining contents of ulp_insn_t arrays. First value is not a + * real instruction; it is a token which is removed by ulp_process_macros_and_load + * function. + */ +#define M_BXZ(label_num) \ + M_BRANCH(label_num), \ + I_BXZI(0) + +/** + * Macro: branch to label if ALU overflow + * + * This macro generates two ulp_insn_t values separated by a comma, and should + * be used when defining contents of ulp_insn_t arrays. First value is not a + * real instruction; it is a token which is removed by ulp_process_macros_and_load + * function. + */ +#define M_BXF(label_num) \ + M_BRANCH(label_num), \ + I_BXFI(0) + + + +#define RTC_SLOW_MEM ((uint32_t*) 0x50000000) /*!< RTC slow memory, 8k size */ + +/** + * @brief Resolve all macro references in a program and load it into RTC memory + * @param load_addr address where the program should be loaded, expressed in 32-bit words + * @param program ulp_insn_t array with the program + * @param psize size of the program, expressed in 32-bit words + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if auxiliary temporary structure can not be allocated + * - one of ESP_ERR_ULP_xxx if program is not valid or can not be loaded + */ +esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize); + +/** + * @brief Load ULP program binary into RTC memory + * + * ULP program binary should have the following format (all values little-endian): + * + * 1. MAGIC, (value 0x00706c75, 4 bytes) + * 2. TEXT_OFFSET, offset of .text section from binary start (2 bytes) + * 3. TEXT_SIZE, size of .text section (2 bytes) + * 4. DATA_SIZE, size of .data section (2 bytes) + * 5. BSS_SIZE, size of .bss section (2 bytes) + * 6. (TEXT_OFFSET - 12) bytes of arbitrary data (will not be loaded into RTC memory) + * 7. .text section + * 8. .data section + * + * Linker script in components/ulp/ld/esp32.ulp.ld produces ELF files which + * correspond to this format. This linker script produces binaries with load_addr == 0. + * + * @param load_addr address where the program should be loaded, expressed in 32-bit words + * @param program_binary pointer to program binary + * @param program_size size of the program binary + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if load_addr is out of range + * - ESP_ERR_INVALID_SIZE if program_size doesn't match (TEXT_OFFSET + TEXT_SIZE + DATA_SIZE) + * - ESP_ERR_NOT_SUPPORTED if the magic number is incorrect + */ +esp_err_t ulp_load_binary(uint32_t load_addr, const uint8_t* program_binary, size_t program_size); + +/** + * @brief Run the program loaded into RTC memory + * @param entry_point entry point, expressed in 32-bit words + * @return ESP_OK on success + */ +esp_err_t ulp_run(uint32_t entry_point); + +/** + * @brief Set one of ULP wakeup period values + * + * ULP coprocessor starts running the program when the wakeup timer counts up + * to a given value (called period). There are 5 period values which can be + * programmed into SENS_ULP_CP_SLEEP_CYCx_REG registers, x = 0..4. + * By default, wakeup timer will use the period set into SENS_ULP_CP_SLEEP_CYC0_REG, + * i.e. period number 0. ULP program code can use SLEEP instruction to select + * which of the SENS_ULP_CP_SLEEP_CYCx_REG should be used for subsequent wakeups. + * + * @param period_index wakeup period setting number (0 - 4) + * @param period_us wakeup period, us + * @note The ULP FSM requires two clock cycles to wakeup before being able to run the program. + * Then additional 16 cycles are reserved after wakeup waiting until the 8M clock is stable. + * The FSM also requires two more clock cycles to go to sleep after the program execution is halted. + * The minimum wakeup period that may be set up for the ULP + * is equal to the total number of cycles spent on the above internal tasks. + * For a default configuration of the ULP running at 150kHz it makes about 133us. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if period_index is out of range + */ +esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us); + +#ifdef __cplusplus +} +#endif diff --git a/MicroPython_BUILD/firmware/esp32/sdkconfig b/MicroPython_BUILD/sdkconfig.ulp similarity index 94% rename from MicroPython_BUILD/firmware/esp32/sdkconfig rename to MicroPython_BUILD/sdkconfig.ulp index f886b154..53320f49 100644 --- a/MicroPython_BUILD/firmware/esp32/sdkconfig +++ b/MicroPython_BUILD/sdkconfig.ulp @@ -34,7 +34,7 @@ CONFIG_FLASH_ENCRYPTION_ENABLED= # # Serial flasher config # -CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" +CONFIG_ESPTOOLPY_PORT="/dev/tty.SLAB_USBtoUART" CONFIG_ESPTOOLPY_BAUD_115200B= CONFIG_ESPTOOLPY_BAUD_230400B= CONFIG_ESPTOOLPY_BAUD_921600B=y @@ -108,7 +108,7 @@ CONFIG_MICROPY_RX_BUFFER_SIZE=1080 CONFIG_MICROPY_USE_BOTH_CORES= CONFIG_MICROPY_TASK_PRIORITY=5 CONFIG_MICROPY_STACK_SIZE=20 -CONFIG_MICROPY_HEAP_SIZE=80 +CONFIG_MICROPY_HEAP_SIZE=3072 CONFIG_MICROPY_THREAD_MAX_THREADS=4 CONFIG_MICROPY_THREAD_STACK_SIZE=4 CONFIG_MICROPY_USE_TELNET=y @@ -131,20 +131,23 @@ CONFIG_MICROPY_FTPSERVER_BUFFER_SIZE=1024 # Modules # CONFIG_MICROPY_PY_FRAMEBUF=y -CONFIG_MICROPY_PY_USE_BTREE= -CONFIG_MICROPY_USE_WEBSOCKETS= +CONFIG_MICROPY_PY_USE_BTREE=y +CONFIG_MICROPY_USE_WEBSOCKETS=y CONFIG_MICROPY_USE_DISPLAY=y CONFIG_MICROPY_USE_TFT=y CONFIG_MICROPY_USE_EPD= CONFIG_MICROPY_USE_EVE= -CONFIG_MICROPY_USE_GSM= +CONFIG_MICROPY_USE_GSM=y CONFIG_MICROPY_USE_GPS=y CONFIG_MICROPY_GPS_SERVICE_STACK=3072 CONFIG_MICROPY_USE_ETHERNET= CONFIG_MICROPY_USE_MDNS=y CONFIG_MICROPY_USE_REQUESTS=y -CONFIG_MICROPY_USE_CURL= -CONFIG_MICROPY_USE_SSH= +CONFIG_MICROPY_USE_CURL=y +CONFIG_MICROPY_CURL_MAX_WRITE_SIZE=8192 +CONFIG_MICROPY_USE_CURL_TLS=y +CONFIG_MICROPY_USE_CURLFTP=y +CONFIG_MICROPY_USE_SSH=y CONFIG_MICROPY_USE_MQTT=y # @@ -251,7 +254,22 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_80= CONFIG_ESP32_DEFAULT_CPU_FREQ_160= CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 -CONFIG_SPIRAM_SUPPORT= +CONFIG_SPIRAM_SUPPORT=y + +# +# SPI RAM config +# +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_IGNORE_NOTFOUND=y +CONFIG_SPIRAM_USE_MEMMAP= +CONFIG_SPIRAM_USE_CAPS_ALLOC=y +CONFIG_SPIRAM_USE_MALLOC= +CONFIG_SPIRAM_TYPE_ESPPSRAM32=y +CONFIG_SPIRAM_SIZE=4194304 +CONFIG_SPIRAM_SPEED_40M=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_CACHE_WORKAROUND=y +CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST=y CONFIG_MEMMAP_TRACEMEM= CONFIG_MEMMAP_TRACEMEM_TWOBANKS= CONFIG_ESP32_TRAX= @@ -280,8 +298,8 @@ CONFIG_CONSOLE_UART_CUSTOM= CONFIG_CONSOLE_UART_NONE= CONFIG_CONSOLE_UART_NUM=0 CONFIG_CONSOLE_UART_BAUDRATE=115200 -CONFIG_ULP_COPROC_ENABLED= -CONFIG_ULP_COPROC_RESERVE_MEM=0 +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_RESERVE_MEM=1024 CONFIG_ESP32_PANIC_PRINT_HALT=y CONFIG_ESP32_PANIC_PRINT_REBOOT= CONFIG_ESP32_PANIC_SILENT_REBOOT= @@ -329,10 +347,10 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y # CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=8 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16 -CONFIG_ESP32_WIFI_STATIC_TX_BUFFER= -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y -CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER= +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_CSI_ENABLED= CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP32_WIFI_TX_BA_WIN=6 @@ -540,7 +558,7 @@ CONFIG_LWIP_MAX_RAW_PCBS=16 # # mbedTLS # -CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=8192 +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 CONFIG_MBEDTLS_DEBUG= CONFIG_MBEDTLS_HARDWARE_AES=y CONFIG_MBEDTLS_HARDWARE_MPI=