diff --git a/.gitignore b/.gitignore index 5e841a89c059d..ef65acbee0e16 100644 --- a/.gitignore +++ b/.gitignore @@ -19,10 +19,12 @@ # VIM Swap Files ###################### *.swp +*.un~ # Build directory ###################### build/ +generated/ # Test failure outputs ###################### diff --git a/lib/utils/printf.c b/lib/utils/printf.c index 1ceeea39ffd0f..c622d1a76146a 100644 --- a/lib/utils/printf.c +++ b/lib/utils/printf.c @@ -99,7 +99,7 @@ STATIC void strn_print_strn(void *data, const char *str, size_t len) { strn_print_env->remain -= len; } -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9 // uClibc requires this alias to be defined, or there may be link errors // when linkings against it statically. int __GI_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) __attribute__((weak, alias ("vsnprintf"))); diff --git a/ports/fupy/Makefile b/ports/fupy/Makefile new file mode 100755 index 0000000000000..e8ace7ef5c3b4 --- /dev/null +++ b/ports/fupy/Makefile @@ -0,0 +1,140 @@ +BUILDINC_DIRECTORY ?= . +ifeq ($(wildcard $(BUILDINC_DIRECTORY)/generated/variables.mak),) +define ERROR_MSG + +$(BUILDINC_DIRECTORY)/generated/variables.mak not found. + +MicroPython for LiteX requires these generated files to build. It +is important that the files match the gateware you are using and +are specific to each board / platform. + +The LiteX generated files are found in build output directory +under "software/include/generated". + +To allow MicroPython to find these files, you must do one of the +following; + * Set BUILDINC_DIRECTORY. + * Copy the files into a local generated directory. + * Run ./get-gateware.sh script (setting PLATFORM/TARGET value). + +endef +$(error $(ERROR_MSG)) +endif # no generated/variables.mak +include $(BUILDINC_DIRECTORY)/generated/variables.mak + +include ../../py/mkenv.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +# include py core make definitions +include ../../py/py.mk + +CROSS_COMPILE ?= $(TRIPLE)- + +INC += -I. +INC += -I../.. +INC += -I../../lib/mp-readline +INC += -I$(BUILD) +INC += -I$(BUILDINC_DIRECTORY) + +# Ensure assembler can also found our custom include files, for crt0 +ASFLAGS += -I$(BUILDINC_DIRECTORY) + +CFLAGS += $(CPUFLAGS) +CFLAGS += $(INC) -Wall -Werror -std=gnu11 -ggdb $(COPT) +CFLAGS += -Og -Wdouble-promotion -Wall -Werror + +ifneq ($(DEBUG), 1) +CFLAGS += -DNDEBUG +endif + +ifeq ($(COPY_TO_MAIN_RAM), 1) +LDFLAGS += -nostartfiles -T litex.ld -Wl,-Map=$@.map -Wl,--cref +else +LDFLAGS += -nostartfiles -T litex-flash.ld -Wl,-Map=$@.map -Wl,--cref +endif + +LDFLAGS += -L$(BUILDINC_DIRECTORY) +LIBS = -Wl,-lc -Wl,-lm + +SRC_C = \ + main.c \ + uart.c \ + isr.c \ + modmachine.c \ + modlitex.c \ + litex_leds.c \ + litex_switches.c \ + lib/utils/stdout_helpers.c \ + lib/utils/interrupt_char.c \ + lib/utils/pyexec.c \ + lib/libc/string0.c \ + lib/mp-readline/readline.c + +# FIXME: Should use crt0 from newlib? However it seems to cause memory access +# at 0x0 + +ifeq ($(COPY_TO_MAIN_RAM), 1) +CRT0_S ?= $(LIBBASE_DIRECTORY)/crt0-$(CPU).S +CRT0_O = $(BUILD)/crt0-$(CPU)-ctr.o +CRT0FLAGS = +else ifeq ($(EXECUTE_IN_PLACE), 1) +CRT0_S ?= $(LIBBASE_DIRECTORY)/crt0-$(CPU).S +CRT0_O = $(BUILD)/crt0-$(CPU)-xip.o +CRT0FLAGS = -DEXECUTE_IN_PLACE +else +$(error You need to define one of COPY_TO_MAIN_RAM or EXECUTE_IN_PLACE to 1) +endif + +# CPU-specific overrides. +ifeq ($(CPU), picorv32) +# Extract the arch-specific flags, otherwise firmware won't link. +LDFLAGS += $(filter -march=% -mabi=%, $(CPUFLAGS)) +# picorv32 needs a special assembly include to compile crt0. +ASFLAGS += -I$(SOC_DIRECTORY)/software/include/base +else ifeq ($(CPU), vexriscv) +LDFLAGS += $(filter -march=% -mabi=%, $(CPUFLAGS)) +endif + +# No extra assembly files for now. +SRC_S = + +OBJ = $(CRT0_O) $(addprefix $(BUILD)/, $(SRC_S:.S=.o)) $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) + +all: $(BUILD)/firmware.bin + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(SRC_LIB) + +# crt0 override for normal assembler rule- needed to distinguish +# whether we need to assemble a crt0 that copies a data section to RAM from +# flash, or a crt0 that assumes a bootloader does it ahead of time. +$(CRT0_O): $(CRT0_S) + $(CC) -c $(ASFLAGS) $(CFLAGS) $(CRT0FLAGS) -o $@ $< + +$(BUILD)/_frozen_mpy.c: frozentest.mpy $(BUILD)/genhdr/qstrdefs.generated.h + $(ECHO) "MISC freezing bytecode" + $(Q)../tools/mpy-tool.py -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h -mlongint-impl=none $< > $@ + +$(BUILD)/firmware.elf: $(OBJ) + $(ECHO) "LINK $@" + $(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(Q)$(OBJCOPY) -O binary $^ $(BUILD)/firmware.bin + +# Run emulation build on a POSIX system with suitable terminal settings +run: + stty raw opost -echo + build/firmware.elf + @echo Resetting terminal... +# This sleep is useful to spot segfaults + sleep 1 + reset + +test: $(BUILD)/firmware.elf + $(Q)/bin/echo -e "print('hello world!', list(x+1 for x in range(10)), end='eol\\\\n')\\r\\n\\x04" | $(BUILD)/firmware.elf | tail -n2 | grep "^hello world! \\[1, 2, 3, 4, 5, 6, 7, 8, 9, 10\\]eol" + +include ../../py/mkrules.mk diff --git a/ports/fupy/README.md b/ports/fupy/README.md new file mode 100644 index 0000000000000..7c42e082efbbf --- /dev/null +++ b/ports/fupy/README.md @@ -0,0 +1,35 @@ +# The minimal port + +This port is intended to be a minimal MicroPython port that actually runs. +It can run under Linux (or similar) and on any STM32F4xx MCU (eg the pyboard). + +## Building and running Linux version + +By default the port will be built for the host machine: + + $ make + +To run a small test script do: + + $ make run + +## Building for an STM32 MCU + +The Makefile has the ability to build for a Cortex-M CPU, and by default +includes some start-up code for an STM32F4xx MCU and also enables a UART +for communication. To build: + + $ make CROSS=1 + +If you previously built the Linux version, you will need to first run +`make clean` to get rid of incompatible object files. + +Building will produce the build/firmware.dfu file which can be programmed +to an MCU using: + + $ make CROSS=1 deploy + +This version of the build will work out-of-the-box on a pyboard (and +anything similar), and will give you a MicroPython REPL on UART1 at 9600 +baud. Pin PA13 will also be driven high, and this turns on the red LED on +the pyboard. diff --git a/ports/fupy/csr-defs.h b/ports/fupy/csr-defs.h new file mode 100644 index 0000000000000..d98e8dfb7b09c --- /dev/null +++ b/ports/fupy/csr-defs.h @@ -0,0 +1,11 @@ +#ifndef CSR_DEFS__H +#define CSR_DEFS__H + +#define CSR_MSTATUS_MIE 0x8 + +#define CSR_IRQ_MASK 0xBC0 +#define CSR_IRQ_PENDING 0xFC0 + +#define CSR_DCACHE_INFO 0xCC0 + +#endif /* CSR_DEFS__H */ diff --git a/ports/fupy/frozentest.mpy b/ports/fupy/frozentest.mpy new file mode 100644 index 0000000000000..c8345b1910611 Binary files /dev/null and b/ports/fupy/frozentest.mpy differ diff --git a/ports/fupy/frozentest.py b/ports/fupy/frozentest.py new file mode 100644 index 0000000000000..0f99b74297fbb --- /dev/null +++ b/ports/fupy/frozentest.py @@ -0,0 +1,7 @@ +print('uPy') +print('a long string that is not interned') +print('a string that has unicode αβγ chars') +print(b'bytes 1234\x01') +print(123456789) +for i in range(4): + print(i) diff --git a/ports/fupy/get-gateware.sh b/ports/fupy/get-gateware.sh new file mode 100755 index 0000000000000..cedbe1e3dadc3 --- /dev/null +++ b/ports/fupy/get-gateware.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +PLATFORM="${PLATFORM:-mimasv2}" +TARGET="${TARGET:-base}" +CPU="${CPU:-lm32}" +BRANCH="${BRANCH:-master}" + +GITHUB_URL=https://github.com/timvideos/HDMI2USB-firmware-prebuilt/ + +# Automatically figure out the latest revision +if [ "$REV" = "" ]; then + REV=$(svn ls $GITHUB_URL/trunk/archive/$BRANCH | sort | tail -n1 | sed -e's-/$--') + echo "Using revision $REV" +fi + +# archive/master/v0.0.3-696-g2f815c1/minispartan6/base/lm32 +BASE_DIR=archive/$BRANCH/$REV/$PLATFORM/$TARGET/$CPU + +# Get the generated header files +svn export --force $GITHUB_URL/trunk/$BASE_DIR/software/include/generated | grep -v "^Exported" + +# Get the flash file +#svn export --force $SVN_URL/flash.bin + +# Get gateware and bios files (to allow image building) +#svn export --force $SVN_URL/gateware/top.bin gateware.bin +#svn export --force $SVN_URL/software/bios/bios.bin diff --git a/ports/fupy/hw/common.h b/ports/fupy/hw/common.h new file mode 100644 index 0000000000000..af668f74e5bd3 --- /dev/null +++ b/ports/fupy/hw/common.h @@ -0,0 +1,52 @@ +#ifndef __HW_COMMON_H +#define __HW_COMMON_H + +#include + +/* To overwrite CSR accessors, define extern, non-inlined versions + * of csr_read[bwl]() and csr_write[bwl](), and define + * CSR_ACCESSORS_DEFINED. + */ + +#ifndef CSR_ACCESSORS_DEFINED +#define CSR_ACCESSORS_DEFINED + +#ifdef __ASSEMBLER__ +#define MMPTR(x) x +#else /* ! __ASSEMBLER__ */ +#define MMPTR(x) (*((volatile unsigned int *)(x))) + +static inline void csr_writeb(uint8_t value, uint32_t addr) +{ + *((volatile uint8_t *)addr) = value; +} + +static inline uint8_t csr_readb(uint32_t addr) +{ + return *(volatile uint8_t *)addr; +} + +static inline void csr_writew(uint16_t value, uint32_t addr) +{ + *((volatile uint16_t *)addr) = value; +} + +static inline uint16_t csr_readw(uint32_t addr) +{ + return *(volatile uint16_t *)addr; +} + +static inline void csr_writel(uint32_t value, uint32_t addr) +{ + *((volatile uint32_t *)addr) = value; +} + +static inline uint32_t csr_readl(uint32_t addr) +{ + return *(volatile uint32_t *)addr; +} +#endif /* ! __ASSEMBLER__ */ + +#endif /* ! CSR_ACCESSORS_DEFINED */ + +#endif /* __HW_COMMON_H */ diff --git a/ports/fupy/hw/ethmac_mem.h b/ports/fupy/hw/ethmac_mem.h new file mode 100644 index 0000000000000..03c7b96d0d7cf --- /dev/null +++ b/ports/fupy/hw/ethmac_mem.h @@ -0,0 +1,11 @@ +#ifndef __HW_ETHMAC_MEM_H +#define __HW_ETHMAC_MEM_H + +#include + +#define ETHMAC_RX0_BASE ETHMAC_BASE +#define ETHMAC_RX1_BASE (ETHMAC_BASE+0x0800) +#define ETHMAC_TX0_BASE (ETHMAC_BASE+0x1000) +#define ETHMAC_TX1_BASE (ETHMAC_BASE+0x1800) + +#endif diff --git a/ports/fupy/hw/flags.h b/ports/fupy/hw/flags.h new file mode 100644 index 0000000000000..911a1b68d2bd5 --- /dev/null +++ b/ports/fupy/hw/flags.h @@ -0,0 +1,40 @@ +#ifndef __HW_FLAGS_H +#define __HW_FLAGS_H + +#define UART_EV_TX 0x1 +#define UART_EV_RX 0x2 + +#define DFII_CONTROL_SEL 0x01 +#define DFII_CONTROL_CKE 0x02 +#define DFII_CONTROL_ODT 0x04 +#define DFII_CONTROL_RESET_N 0x08 + +#define DFII_COMMAND_CS 0x01 +#define DFII_COMMAND_WE 0x02 +#define DFII_COMMAND_CAS 0x04 +#define DFII_COMMAND_RAS 0x08 +#define DFII_COMMAND_WRDATA 0x10 +#define DFII_COMMAND_RDDATA 0x20 + +#define ETHMAC_EV_SRAM_WRITER 0x1 +#define ETHMAC_EV_SRAM_READER 0x1 + +#define CLKGEN_STATUS_BUSY 0x1 +#define CLKGEN_STATUS_PROGDONE 0x2 +#define CLKGEN_STATUS_LOCKED 0x4 + +#define DVISAMPLER_TOO_LATE 0x1 +#define DVISAMPLER_TOO_EARLY 0x2 + +#define DVISAMPLER_DELAY_MASTER_CAL 0x01 +#define DVISAMPLER_DELAY_MASTER_RST 0x02 +#define DVISAMPLER_DELAY_SLAVE_CAL 0x04 +#define DVISAMPLER_DELAY_SLAVE_RST 0x08 +#define DVISAMPLER_DELAY_INC 0x10 +#define DVISAMPLER_DELAY_DEC 0x20 + +#define DVISAMPLER_SLOT_EMPTY 0 +#define DVISAMPLER_SLOT_LOADED 1 +#define DVISAMPLER_SLOT_PENDING 2 + +#endif /* __HW_FLAGS_H */ diff --git a/ports/fupy/irq.h b/ports/fupy/irq.h new file mode 100644 index 0000000000000..19f00b1c55b34 --- /dev/null +++ b/ports/fupy/irq.h @@ -0,0 +1,144 @@ +#ifndef __IRQ_H +#define __IRQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __picorv32__ +// PicoRV32 has a very limited interrupt support, implemented via custom +// instructions. It also doesn't have a global interrupt enable/disable, so +// we have to emulate it via saving and restoring a mask and using 0/~1 as a +// hardware mask. +// Due to all this somewhat low-level mess, all of the glue is implemented in +// the RiscV crt0, and this header is kept as a thin wrapper. Since interrupts +// managed by this layer, do not call interrupt instructions directly, as the +// state will go out of sync with the hardware. + +// Read only. +extern unsigned int _irq_pending; +// Read only. +extern unsigned int _irq_mask; +// Read only. +extern unsigned int _irq_enabled; +extern void _irq_enable(void); +extern void _irq_disable(void); +extern void _irq_setmask(unsigned int); +#endif + +static inline unsigned int irq_getie(void) +{ +#if defined (__lm32__) + unsigned int ie; + __asm__ __volatile__("rcsr %0, IE" : "=r" (ie)); + return ie; +#elif defined (__or1k__) + return !!(mfspr(SPR_SR) & SPR_SR_IEE); +#elif defined (__picorv32__) + return _irq_enabled != 0; +#elif defined (__vexriscv__) + return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0; +#elif defined (__minerva__) + return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0; +#else +#error Unsupported architecture +#endif +} + +static inline void irq_setie(unsigned int ie) +{ +#if defined (__lm32__) + __asm__ __volatile__("wcsr IE, %0" : : "r" (ie)); +#elif defined (__or1k__) + if (ie & 0x1) + mtspr(SPR_SR, mfspr(SPR_SR) | SPR_SR_IEE); + else + mtspr(SPR_SR, mfspr(SPR_SR) & ~SPR_SR_IEE); +#elif defined (__picorv32__) + if (ie & 0x1) + _irq_enable(); + else + _irq_disable(); +#elif defined (__vexriscv__) + if(ie) csrs(mstatus,CSR_MSTATUS_MIE); else csrc(mstatus,CSR_MSTATUS_MIE); +#elif defined (__minerva__) + if(ie) csrs(mstatus,CSR_MSTATUS_MIE); else csrc(mstatus,CSR_MSTATUS_MIE); +#else +#error Unsupported architecture +#endif +} + +static inline unsigned int irq_getmask(void) +{ +#if defined (__lm32__) + unsigned int mask; + __asm__ __volatile__("rcsr %0, IM" : "=r" (mask)); + return mask; +#elif defined (__or1k__) + return mfspr(SPR_PICMR); +#elif defined (__picorv32__) + // PicoRV32 interrupt mask bits are high-disabled. This is the inverse of how + // LiteX sees things. + return ~_irq_mask; +#elif defined (__vexriscv__) + unsigned int mask; + asm volatile ("csrr %0, %1" : "=r"(mask) : "i"(CSR_IRQ_MASK)); + return mask; +#elif defined (__minerva__) + unsigned int mask; + asm volatile ("csrr %0, %1" : "=r"(mask) : "i"(CSR_IRQ_MASK)); + return mask; +#else +#error Unsupported architecture +#endif +} + +static inline void irq_setmask(unsigned int mask) +{ +#if defined (__lm32__) + __asm__ __volatile__("wcsr IM, %0" : : "r" (mask)); +#elif defined (__or1k__) + mtspr(SPR_PICMR, mask); +#elif defined (__picorv32__) + // PicoRV32 interrupt mask bits are high-disabled. This is the inverse of how + // LiteX sees things. + _irq_setmask(~mask); +#elif defined (__vexriscv__) + asm volatile ("csrw %0, %1" :: "i"(CSR_IRQ_MASK), "r"(mask)); +#elif defined (__minerva__) + asm volatile ("csrw %0, %1" :: "i"(CSR_IRQ_MASK), "r"(mask)); +#else +#error Unsupported architecture +#endif +} + +static inline unsigned int irq_pending(void) +{ +#if defined (__lm32__) + unsigned int pending; + __asm__ __volatile__("rcsr %0, IP" : "=r" (pending)); + return pending; +#elif defined (__or1k__) + return mfspr(SPR_PICSR); +#elif defined (__picorv32__) + return _irq_pending; +#elif defined (__vexriscv__) + unsigned int pending; + asm volatile ("csrr %0, %1" : "=r"(pending) : "i"(CSR_IRQ_PENDING)); + return pending; +#elif defined (__minerva__) + unsigned int pending; + asm volatile ("csrr %0, %1" : "=r"(pending) : "i"(CSR_IRQ_PENDING)); + return pending; +#else +#error Unsupported architecture +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* __IRQ_H */ diff --git a/ports/fupy/isr.c b/ports/fupy/isr.c new file mode 100644 index 0000000000000..293109cc40c56 --- /dev/null +++ b/ports/fupy/isr.c @@ -0,0 +1,13 @@ +#include +#include +#include + +void isr(void) +{ + unsigned int irqs; + + irqs = irq_pending() & irq_getmask(); + + if(irqs & (1 << UART_INTERRUPT)) + uart_isr(); +} diff --git a/ports/fupy/litex-flash.ld b/ports/fupy/litex-flash.ld new file mode 100755 index 0000000000000..88a4c35d32056 --- /dev/null +++ b/ports/fupy/litex-flash.ld @@ -0,0 +1,65 @@ +INCLUDE generated/output_format.ld +INCLUDE generated/regions.ld +ENTRY(_start) +/* +INPUT(crti.o crtbegin.o crt0.o crtend.o crtn.o) +GROUP(-lgloss -lnosys -lc -lgcc) +*/ +__DYNAMIC = 0; + +SECTIONS +{ + .fbi : + { + . = . + 8; /* Firmware Base Image format (len/crc) data comes first. */ + } > user_flash + + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > user_flash + + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + /* On RISCV, normally placed near _gp. Keeping RAM footprint low + is higher priority than speed here. */ + *(.srodata .srodata.*) + _erodata = .; + } > user_flash + + .data : AT (ADDR(.rodata) + SIZEOF (.rodata)) + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + _gp = ALIGN(16); + *(.sdata .sdata.* .gnu.linkonce.s.*) + _edata = ALIGN(16); /* Make sure _edata is >= _gp. */ + } > sram + + .bss : + { + . = ALIGN(4); + _fbss = .; + __bss_start = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + _end = .; + end = .; + } > sram +} + +PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 4); diff --git a/ports/fupy/litex.ld b/ports/fupy/litex.ld new file mode 100644 index 0000000000000..9e2691efd336b --- /dev/null +++ b/ports/fupy/litex.ld @@ -0,0 +1,58 @@ +INCLUDE generated/output_format.ld +INCLUDE generated/regions.ld +ENTRY(_start) +/* +INPUT(crti.o crtbegin.o crt0.o crtend.o crtn.o) +GROUP(-lgloss -lnosys -lc -lgcc) +*/ +__DYNAMIC = 0; + +SECTIONS +{ + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > main_ram + + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > main_ram + + .data : + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + _gp = ALIGN(16); + *(.sdata .sdata.* .gnu.linkonce.s.*) + *(.srodata .srodata.*) + _edata = .; + } > main_ram + + .bss : + { + . = ALIGN(4); + _fbss = .; + __bss_start = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + _end = .; + end = .; + } > sram +} + +PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 4); diff --git a/ports/fupy/litex_leds.c b/ports/fupy/litex_leds.c new file mode 100644 index 0000000000000..19161b3c2c4fb --- /dev/null +++ b/ports/fupy/litex_leds.c @@ -0,0 +1,98 @@ + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "py/objexcept.h" + +#include "generated/csr.h" + +#if !defined(CSR_CAS_BASE) || !defined(CSR_CAS_LEDS_OUT_ADDR) +static inline unsigned char cas_leds_out_read(void) { + return 0; +} + +static inline void cas_leds_out_write(unsigned char value) { +} +#endif + +const mp_obj_type_t litex_led_type; + +typedef struct _litex_led_obj_t { + mp_obj_base_t base; + int num; +} litex_led_obj_t; + +STATIC litex_led_obj_t litex_leds[8] = { + {{&litex_led_type}, 1}, + {{&litex_led_type}, 2}, + {{&litex_led_type}, 3}, + {{&litex_led_type}, 4}, + {{&litex_led_type}, 5}, + {{&litex_led_type}, 6}, + {{&litex_led_type}, 7}, + {{&litex_led_type}, 8} +}; + +STATIC mp_obj_t litex_led_make_new(const mp_obj_type_t *type_in, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_uint_t led_num = mp_obj_get_int(args[0]); + + switch (led_num) { + case 1 ... 8: + return &litex_leds[led_num - 1]; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "not a valid LED number: %d", led_num)); + } +} + +void litex_led_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + litex_led_obj_t *self = self_in; + mp_printf(print, "LED(%u)", self->num); +} + +STATIC mp_obj_t litex_led_read(mp_obj_t self_in) { + litex_led_obj_t *led = self_in; + bool state = cas_leds_out_read() & (1 << (led->num - 1)); + + return mp_obj_new_bool(state); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(litex_led_read_obj, litex_led_read); + +STATIC mp_obj_t litex_led_on(mp_obj_t self_in) { + litex_led_obj_t *led = self_in; + char value = cas_leds_out_read(); + + cas_leds_out_write(value | (1 << (led->num - 1))); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(litex_led_on_obj, litex_led_on); + +STATIC mp_obj_t litex_led_off(mp_obj_t self_in) { + litex_led_obj_t *led = self_in; + char value = cas_leds_out_read(); + + cas_leds_out_write(value & ~(1 << (led->num - 1))); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(litex_led_off_obj, litex_led_off); + + +STATIC const mp_map_elem_t litex_led_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&litex_led_read_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_on), (mp_obj_t)&litex_led_on_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_off), (mp_obj_t)&litex_led_off_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(litex_led_locals_dict, litex_led_locals_dict_table); + +const mp_obj_type_t litex_led_type = { + { &mp_type_type }, + .name = MP_QSTR_LED, + .print = litex_led_print, + .make_new = litex_led_make_new, + .locals_dict = (mp_obj_t)&litex_led_locals_dict, +}; diff --git a/ports/fupy/litex_switches.c b/ports/fupy/litex_switches.c new file mode 100644 index 0000000000000..5d2a85bd718f2 --- /dev/null +++ b/ports/fupy/litex_switches.c @@ -0,0 +1,74 @@ + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "py/objexcept.h" + +#include "generated/csr.h" + +#if !defined(CSR_CAS_BASE) || !defined(CSR_CAS_SWITCHES_IN_ADDR) +static inline unsigned char cas_switches_in_read(void) { + return 0; +} +#endif + +const mp_obj_type_t litex_switch_type; + +typedef struct _litex_switch_obj_t { + mp_obj_base_t base; + int num; +} litex_switch_obj_t; + +STATIC litex_switch_obj_t litex_switches[8] = { + {{&litex_switch_type}, 1}, + {{&litex_switch_type}, 2}, + {{&litex_switch_type}, 3}, + {{&litex_switch_type}, 4}, + {{&litex_switch_type}, 5}, + {{&litex_switch_type}, 6}, + {{&litex_switch_type}, 7}, + {{&litex_switch_type}, 8} +}; + +STATIC mp_obj_t litex_switch_make_new(const mp_obj_type_t *type_in, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_uint_t switch_num = mp_obj_get_int(args[0]); + + switch (switch_num) { + case 1 ... 8: + return &litex_switches[switch_num - 1]; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "not a valid SWITCH number: %d", switch_num)); + } +} + +void litex_switch_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + litex_switch_obj_t *self = self_in; + mp_printf(print, "SWITCH(%u)", self->num); +} + +STATIC mp_obj_t litex_switch_read(mp_obj_t self_in) { + litex_switch_obj_t *sw = self_in; + bool state = cas_switches_in_read() & (1 << (sw->num - 1)); + + return mp_obj_new_bool(state); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(litex_switch_read_obj, litex_switch_read); + + + +STATIC const mp_map_elem_t litex_switches_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&litex_switch_read_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(litex_switches_locals_dict, litex_switches_locals_dict_table); + +const mp_obj_type_t litex_switch_type = { + { &mp_type_type }, + .name = MP_QSTR_SWITCH, + .print = litex_switch_print, + .make_new = litex_switch_make_new, + .locals_dict = (mp_obj_t)&litex_switches_locals_dict, +}; diff --git a/ports/fupy/main.c b/ports/fupy/main.c new file mode 100644 index 0000000000000..93278fa4e266f --- /dev/null +++ b/ports/fupy/main.c @@ -0,0 +1,98 @@ +#include +#include +#include + +#include "py/nlr.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "lib/utils/pyexec.h" + +#include "mphalport.h" + +#include "generated/csr.h" + +void do_str(const char *src, mp_parse_input_kind_t input_kind) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); + if (lex == NULL) { + printf("MemoryError: lexer could not allocate memory\n"); + return; + } + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + qstr source_name = lex->source_name; + mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); + mp_call_function_0(module_fun); + nlr_pop(); + } else { + // uncaught exception + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +} + +static char *stack_top; +static char heap[2048]; + +#include "irq.h" + +int main(int argc, char **argv) { + int stack_dummy; + stack_top = (char*)&stack_dummy; + + irq_setmask(0); + irq_setie(1); + uart_init(); + + #if MICROPY_ENABLE_GC + gc_init(heap, heap + sizeof(heap)); + #endif + mp_init(); + + pyexec_friendly_repl(); + + mp_deinit(); + return 0; +} + +void gc_collect(void) { + // WARNING: This gc_collect implementation doesn't try to get root + // pointers from CPU registers, and thus may function incorrectly. + void *dummy; + gc_collect_start(); + gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t)); + gc_collect_end(); + gc_dump_info(); +} + +mp_lexer_t *mp_lexer_new_from_file(const char *filename) { + return NULL; +} + +mp_import_stat_t mp_import_stat(const char *path) { + return MP_IMPORT_STAT_NO_EXIST; +} + +mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); + +void NORETURN __fatal_error(const char *msg) { + while (1); +} + +void nlr_jump_fail(void *val) { + printf("FATAL: uncaught exception %p\n", val); + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)val); + __fatal_error(""); +} + +#ifndef NDEBUG +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + __fatal_error("Assertion failed"); +} +#endif diff --git a/ports/fupy/modlitex.c b/ports/fupy/modlitex.c new file mode 100644 index 0000000000000..85f1c92977146 --- /dev/null +++ b/ports/fupy/modlitex.c @@ -0,0 +1,18 @@ +#include "py/obj.h" + +extern const mp_obj_type_t litex_led_type; +extern const mp_obj_type_t litex_switch_type; + +STATIC const mp_rom_map_elem_t litex_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_litex) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&litex_led_type) }, + { MP_ROM_QSTR(MP_QSTR_SWITCH), MP_ROM_PTR(&litex_switch_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(litex_module_globals, litex_module_globals_table); + +const mp_obj_module_t mp_module_litex = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&litex_module_globals, +}; diff --git a/ports/fupy/modmachine.c b/ports/fupy/modmachine.c new file mode 100644 index 0000000000000..931fb0f86963a --- /dev/null +++ b/ports/fupy/modmachine.c @@ -0,0 +1,26 @@ + +#include "py/runtime.h" +#include "py/obj.h" + +#include "extmod/machine_mem.h" +#include "extmod/machine_pinbase.h" +#include "extmod/machine_pulse.h" + +#if MICROPY_PY_MACHINE + +STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, + + { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&machine_module_globals, +}; + +#endif // MICROPY_PY_MACHINE diff --git a/ports/fupy/mpconfigport.h b/ports/fupy/mpconfigport.h new file mode 100644 index 0000000000000..c582f487dbffa --- /dev/null +++ b/ports/fupy/mpconfigport.h @@ -0,0 +1,97 @@ +#include + +// options to control how Micro Python is built + +#define MICROPY_QSTR_BYTES_IN_HASH (1) +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) +#define MICROPY_EMIT_X64 (0) +#define MICROPY_EMIT_THUMB (0) +#define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_COMP_MODULE_CONST (0) +#define MICROPY_COMP_CONST (0) +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0) +#define MICROPY_MEM_STATS (0) +#define MICROPY_DEBUG_PRINTERS (0) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_GC_ALLOC_THRESHOLD (0) +#define MICROPY_REPL_EVENT_DRIVEN (0) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_HELPER_LEXER_UNIX (0) +#define MICROPY_ENABLE_SOURCE_LINE (0) +#define MICROPY_ENABLE_DOC_STRING (0) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) +#define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_BUILTINS_BYTEARRAY (0) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (0) +#define MICROPY_PY_BUILTINS_ENUMERATE (0) +#define MICROPY_PY_BUILTINS_FILTER (0) +#define MICROPY_PY_BUILTINS_FROZENSET (0) +#define MICROPY_PY_BUILTINS_REVERSED (0) +#define MICROPY_PY_BUILTINS_SET (0) +#define MICROPY_PY_BUILTINS_SLICE (0) +#define MICROPY_PY_BUILTINS_PROPERTY (0) +#define MICROPY_PY_BUILTINS_MIN_MAX (0) +#define MICROPY_PY___FILE__ (0) +#define MICROPY_PY_GC (0) +#define MICROPY_PY_ARRAY (0) +#define MICROPY_PY_ATTRTUPLE (0) +#define MICROPY_PY_COLLECTIONS (0) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_IO (0) +#define MICROPY_PY_STRUCT (0) +#define MICROPY_PY_SYS (1) +#define MICROPY_MODULE_FROZEN_MPY (0) +#define MICROPY_CPYTHON_COMPAT (0) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) + +#define MICROPY_PY_MACHINE (1) +#define MICROPY_KBD_EXCEPTION (1) + +// type definitions for the specific machine + +#define BYTES_PER_WORD (4) + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1)) + +// This port is intended to be 32-bit, but unfortunately, int32_t for +// different targets may be defined in different ways - either as int +// or as long. This requires different printf formatting specifiers +// to print such value. So, we avoid int32_t and use int directly. +#define UINT_FMT "%u" +#define INT_FMT "%d" +typedef int mp_int_t; // must be pointer size +typedef unsigned mp_uint_t; // must be pointer size + +typedef long mp_off_t; + +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) + +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, + +extern const struct _mp_obj_module_t mp_module_machine; +extern const struct _mp_obj_module_t mp_module_litex; +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ + { MP_ROM_QSTR(MP_QSTR_litex), MP_ROM_PTR(&mp_module_litex) }, \ + +// We need to provide a declaration/definition of alloca() +#include + +#define MICROPY_HW_BOARD_NAME "litex" +#define MICROPY_HW_MCU_NAME "lm32" + +#ifdef __linux__ +#define MICROPY_MIN_USE_STDOUT (1) +#endif + +#define MP_STATE_PORT MP_STATE_VM + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; diff --git a/ports/fupy/mphalport.h b/ports/fupy/mphalport.h new file mode 100644 index 0000000000000..bc12db1579921 --- /dev/null +++ b/ports/fupy/mphalport.h @@ -0,0 +1,17 @@ +#include "uart.h" +#include "lib/utils/interrupt_char.h" + +static inline mp_uint_t mp_hal_ticks_ms(void) { return 0; } + + +// Receive single character +static inline int mp_hal_stdin_rx_chr(void) { + return uart_read(); +} + +// Send string of given length +static inline void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + while (len--) { + uart_write(*str++); + } +} diff --git a/ports/fupy/qstrdefsport.h b/ports/fupy/qstrdefsport.h new file mode 100644 index 0000000000000..3ba897069bf73 --- /dev/null +++ b/ports/fupy/qstrdefsport.h @@ -0,0 +1 @@ +// qstrs specific to this port diff --git a/ports/fupy/stm32f405.ld b/ports/fupy/stm32f405.ld new file mode 100644 index 0000000000000..b4aeda744e388 --- /dev/null +++ b/ports/fupy/stm32f405.ld @@ -0,0 +1,63 @@ +/* + GNU linker script for STM32F405 +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x100000 /* entire flash, 1 MiB */ + CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 0x010000 /* 64 KiB */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x020000 /* 128 KiB */ +} + +/* top end of the stack */ +_estack = ORIGIN(RAM) + LENGTH(RAM); + +/* define output sections */ +SECTIONS +{ + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* isr vector table */ + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + + . = ALIGN(4); + _etext = .; /* define a global symbol at end of code */ + _sidata = _etext; /* This is used by the startup in order to initialize the .data secion */ + } >FLASH + + /* This is the initialized data section + The program executes knowing that the data is in the RAM + but the loader puts the initial values in the FLASH (inidata). + It is one task of the startup to copy the initial values from FLASH to RAM. */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ + } >RAM + + /* Uninitialized data section */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* define a global symbol at bss start; used by startup code */ + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end; used by startup code */ + } >RAM + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/ports/fupy/system.h b/ports/fupy/system.h new file mode 100644 index 0000000000000..2d108d2c1a5dd --- /dev/null +++ b/ports/fupy/system.h @@ -0,0 +1,59 @@ +#ifndef __SYSTEM_H +#define __SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +void flush_cpu_icache(void); +void flush_cpu_dcache(void); +void flush_l2_cache(void); + +#ifdef __or1k__ +#include +static inline unsigned long mfspr(unsigned long add) +{ + unsigned long ret; + + __asm__ __volatile__ ("l.mfspr %0,%1,0" : "=r" (ret) : "r" (add)); + + return ret; +} + +static inline void mtspr(unsigned long add, unsigned long val) +{ + __asm__ __volatile__ ("l.mtspr %0,%1,0" : : "r" (add), "r" (val)); +} +#endif + + +#if defined(__vexriscv__) || defined(__minerva__) +#include +#define csrr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define csrw(reg, val) ({ \ + if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ + asm volatile ("csrw " #reg ", %0" :: "i"(val)); \ + else \ + asm volatile ("csrw " #reg ", %0" :: "r"(val)); }) + +#define csrs(reg, bit) ({ \ + if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrs x0, " #reg ", %0" :: "i"(bit)); \ + else \ + asm volatile ("csrrs x0, " #reg ", %0" :: "r"(bit)); }) + +#define csrc(reg, bit) ({ \ + if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrc x0, " #reg ", %0" :: "i"(bit)); \ + else \ + asm volatile ("csrrc x0, " #reg ", %0" :: "r"(bit)); }) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SYSTEM_H */ diff --git a/ports/fupy/uart.c b/ports/fupy/uart.c new file mode 100644 index 0000000000000..56ea579b5ae25 --- /dev/null +++ b/ports/fupy/uart.c @@ -0,0 +1,119 @@ +#include "uart.h" +#include "irq.h" +#include "generated/csr.h" +#include "hw/flags.h" + +/* + * Buffer sizes must be a power of 2 so that modulos can be computed + * with logical AND. + */ + +#define UART_RINGBUFFER_SIZE_RX 128 +#define UART_RINGBUFFER_MASK_RX (UART_RINGBUFFER_SIZE_RX-1) + +static char rx_buf[UART_RINGBUFFER_SIZE_RX]; +static volatile unsigned int rx_produce; +static unsigned int rx_consume; + +#define UART_RINGBUFFER_SIZE_TX 128 +#define UART_RINGBUFFER_MASK_TX (UART_RINGBUFFER_SIZE_TX-1) + +static char tx_buf[UART_RINGBUFFER_SIZE_TX]; +static unsigned int tx_produce; +static volatile unsigned int tx_consume; + +extern int mp_interrupt_char; +void mp_keyboard_interrupt(void); + +void uart_isr(void) +{ + unsigned int stat, rx_produce_next; + + stat = uart_ev_pending_read(); + + if(stat & UART_EV_RX) { + while(!uart_rxempty_read()) { + rx_produce_next = (rx_produce + 1) & UART_RINGBUFFER_MASK_RX; + if(rx_produce_next != rx_consume) { + char c = uart_rxtx_read(); + if (c == mp_interrupt_char) { + mp_keyboard_interrupt(); + uart_ev_pending_write(UART_EV_RX); + return; + } + rx_buf[rx_produce] = c; + rx_produce = rx_produce_next; + } + uart_ev_pending_write(UART_EV_RX); + } + } + + if(stat & UART_EV_TX) { + uart_ev_pending_write(UART_EV_TX); + while((tx_consume != tx_produce) && !uart_txfull_read()) { + uart_rxtx_write(tx_buf[tx_consume]); + tx_consume = (tx_consume + 1) & UART_RINGBUFFER_MASK_TX; + } + } +} + +/* Do not use in interrupt handlers! */ +char uart_read(void) +{ + char c; + + if(irq_getie()) { + while(rx_consume == rx_produce); + } else if (rx_consume == rx_produce) { + return 0; + } + + c = rx_buf[rx_consume]; + rx_consume = (rx_consume + 1) & UART_RINGBUFFER_MASK_RX; + return c; +} + +int uart_read_nonblock(void) +{ + return (rx_consume != rx_produce); +} + +void uart_write(char c) +{ + unsigned int oldmask; + unsigned int tx_produce_next = (tx_produce + 1) & UART_RINGBUFFER_MASK_TX; + + if(irq_getie()) { + while(tx_produce_next == tx_consume); + } else if(tx_produce_next == tx_consume) { + return; + } + + oldmask = irq_getmask(); + irq_setmask(oldmask & ~(1 << UART_INTERRUPT)); + if((tx_consume != tx_produce) || uart_txfull_read()) { + tx_buf[tx_produce] = c; + tx_produce = tx_produce_next; + } else { + uart_rxtx_write(c); + } + irq_setmask(oldmask); +} + +void uart_init(void) +{ + rx_produce = 0; + rx_consume = 0; + + tx_produce = 0; + tx_consume = 0; + + uart_ev_pending_write(uart_ev_pending_read()); + uart_ev_enable_write(UART_EV_TX | UART_EV_RX); + irq_setmask(irq_getmask() | (1 << UART_INTERRUPT)); +} + +void uart_sync(void) +{ + while(tx_consume != tx_produce); +} diff --git a/ports/fupy/uart.h b/ports/fupy/uart.h new file mode 100644 index 0000000000000..3d8a4fcf4d104 --- /dev/null +++ b/ports/fupy/uart.h @@ -0,0 +1,20 @@ +#ifndef __UART_H +#define __UART_H + +#ifdef __cplusplus +extern "C" { +#endif + +void uart_init(void); +void uart_isr(void); +void uart_sync(void); + +void uart_write(char c); +char uart_read(void); +int uart_read_nonblock(void); + +#ifdef __cplusplus +} +#endif + +#endif