espressif / binutils-esp32ulp Goto Github PK
View Code? Open in Web Editor NEWBinutils fork with support for the ESP32 ULP co-processor
License: GNU General Public License v2.0
Binutils fork with support for the ESP32 ULP co-processor
License: GNU General Public License v2.0
README for GNU development tools This directory contains various GNU compilers, assemblers, linkers, debuggers, etc., plus their support routines, definitions, and documentation. If you are receiving this as part of a GDB release, see the file gdb/README. If with a binutils release, see binutils/README; if with a libg++ release, see libg++/README, etc. That'll give you info about this package -- supported targets, how to use it, how to report bugs, etc. It is now possible to automatically configure and build a variety of tools with one command. To build all of the tools contained herein, run the ``configure'' script here, e.g.: ./configure make To install them (by default in /usr/local/bin, /usr/local/lib, etc), then do: make install (If the configure script can't determine your type of computer, give it the name as an argument, for instance ``./configure sun4''. You can use the script ``config.sub'' to test whether a name is recognized; if it is, config.sub translates it to a triplet specifying CPU, vendor, and OS.) If you have more than one compiler on your system, it is often best to explicitly set CC in the environment before running configure, and to also set CC when running make. For example (assuming sh/bash/ksh): CC=gcc ./configure make A similar example using csh: setenv CC gcc ./configure make Much of the code and documentation enclosed is copyright by the Free Software Foundation, Inc. See the file COPYING or COPYING.LIB in the various directories, for a description of the GNU General Public License terms under which you can copy the files. REPORTING BUGS: Again, see gdb/README, binutils/README, etc., for info on where and how to report problems.
#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/rtc_i2c_reg.h"
#include "soc/sens_reg.h"
#include "soc/soc.h"
.macro WAIT_SEC
# waits for about 1s.
STAGE_RST
bar: WAIT 65535 # maximum according to TRM 29.4.10 - ca. 8.1ms
STAGE_INC 1
JUMPS bar, 122, EQ # ~999.4ms + 122*4 cycles
.endm
.text
init:
// rtc_gpio_init(GPIO_NUM_4)
WRITE_RTC_FIELD(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL, 1)
WRITE_RTC_FIELD(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_SEL, 0)
// rtc_gpio_set_direction(GPIO_NUM_4, RTC_GPIO_MODE_OUTPUT_ONLY);
// rtc_gpio_output_enable(GPIO_NUM_4);
WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + 10, 1, 1)
WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + 10, 1, 0)
# initialize counter
JUMP toggle
toggle:
WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 10, 1, 1)
STAGE_RST
0: WAIT 65535 # maximum according to TRM 29.4.10 - ca. 8.1ms
STAGE_INC 1
JUMPS 0b, 122, EQ # ~999.4ms + 122*4 cycles
WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 10, 1, 1)
WAIT_SEC
jump toggle
The output of RTC GPIO 10 aka GPIO 4 stays high for about a second and then low for about a second.
The output of RTC GPIO 10 stays high for about a second and then low for about 8ms => probably for the WAIT 65535
inside the macro and a few additional cycles.
So it seems that JUMPS
inside the macro WAIT_SEC
doesn't work, which seems to be an issue with the binutils.
I am using the most recent binutils version (esp32ulp-elf-binutils-linux64-d2ae637d.tar.gz).
/bin/bash ./libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -I. -I. -I. -I./../include -DHAVE_x86_64_elf64_vec -DHAVE_i386_elf32_vec -DHAVE_iamcu_elf32_vec -DHAVE_x86_64_elf32_vec -DHAVE_i386_aout_linux_vec -DHAVE_i386_pei_vec -DHAVE_x86_64_pei_vec -DHAVE_l1om_elf64_vec -DHAVE_k1om_elf64_vec -DHAVE_elf64_le_vec -DHAVE_elf64_be_vec -DHAVE_elf32_le_vec -DHAVE_elf32_be_vec -DHAVE_plugin_vec -DBINDIR='"/usr/local/bin"' -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wstack-usage=262144 -Werror -I./../zlib -g -O2 -MT pei-i386.lo -MD -MP -MF .deps/pei-i386.Tpo -c -o pei-i386.lo pei-i386.c
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I. -I. -I./../include -DHAVE_x86_64_elf64_vec -DHAVE_i386_elf32_vec -DHAVE_iamcu_elf32_vec -DHAVE_x86_64_elf32_vec -DHAVE_i386_aout_linux_vec -DHAVE_i386_pei_vec -DHAVE_x86_64_pei_vec -DHAVE_l1om_elf64_vec -DHAVE_k1om_elf64_vec -DHAVE_elf64_le_vec -DHAVE_elf64_be_vec -DHAVE_elf32_le_vec -DHAVE_elf32_be_vec -DHAVE_plugin_vec -DBINDIR=\"/usr/local/bin\" -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wstack-usage=262144 -Werror -I./../zlib -g -O2 -MT pei-i386.lo -MD -MP -MF .deps/pei-i386.Tpo -c pei-i386.c -o pei-i386.o
In file included from coff-i386.c:614:0,
from pei-i386.c:45:
coffcode.h: In function ‘coff_write_object_contents’:
coffcode.h:3775:46: error: ‘%lu’ directive output may be truncated writing between 1 and 20 bytes into a region of size 8 [-Werror=format-truncation=]
snprintf (s_name_buf, SCNNMLEN + 1, "/%lu", (unsigned long) string_size);
^~~
coffcode.h:3775:44: note: directive argument in the range [4, 18446744073709551614]
snprintf (s_name_buf, SCNNMLEN + 1, "/%lu", (unsigned long) string_size);
^~~~~~
In file included from /usr/include/stdio.h:862:0,
from sysdep.h:37,
from pei-i386.c:21:
/usr/include/x86_64-linux-gnu/bits/stdio2.h:64:10: note: ‘__builtin___snprintf_chk’ output between 3 and 22 bytes into a destination of size 9
return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__bos (__s), __fmt, __va_arg_pack ());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
the build fails on FreeBSD. the following is an excerpt of the build log. when looking for the BFD_RELOC_ESP32S2ULP_GOT17M4
in the source, cannot find it in other files. my dumb workaround is just removing the part of the file in question, which is obviously not a solution. the part of the file has not been updated much since the initial import. do you have any idea?
gcc10 -DHAVE_CONFIG_H -I. -I. -I. -I../bfd -I./config -I./../include -I./.. -I./../bfd -DLOCALEDIR="\"/usr/local/binutils-esp32s2ulp/share/locale\"" -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wstack-usage=262144 -Wwrite-strings -I./../zlib -O2 -pipe -fstack-protector-strong -Wl,-rpath=/usr/local/lib/gcc10 -fno-strict-aliasing -MT atof-ieee.o -MD -MP -MF .deps/atof-ieee.Tpo -c -o atof-ieee.o `test -f 'config/atof-ieee.c' || echo './'`config/atof-ieee.c
mv -f .deps/atof-ieee.Tpo .deps/atof-ieee.Po
/bin/sh ./../ylwrap ./config/esp32s2ulp-parse.y y.tab.c esp32s2ulp-parse.c y.tab.h esp32s2ulp-parse.h -- byacc -d ;
byacc: w - line 356 of "/wrkdirs/usr/ports/devel/binutils-esp32s2ulp/work/binutils-esp32ulp-2.28.51-esp-20191205/gas/./config/esp32s2ulp-parse.y", the default action for asm_1 assigns an undefined value to $$
byacc: 12 rules never reduced
updating esp32s2ulp-parse.h
gcc10 -DHAVE_CONFIG_H -I. -I. -I. -I../bfd -I./config -I./../include -I./.. -I./../bfd -DLOCALEDIR="\"/usr/local/binutils-esp32s2ulp/share/locale\"" -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wstack-usage=262144 -Wwrite-strings -I./../zlib -O2 -pipe -fstack-protector-strong -Wl,-rpath=/usr/local/lib/gcc10 -fno-strict-aliasing -MT esp32s2ulp-parse.o -MD -MP -MF .deps/esp32s2ulp-parse.Tpo -c -o esp32s2ulp-parse.o esp32s2ulp-parse.c
./config/esp32s2ulp-parse.y: In function 'yyparse':
./config/esp32s2ulp-parse.y:927:18: error: 'BFD_RELOC_ESP32S2ULP_GOT17M4' undeclared (first use in this function); did you mean 'BFD_RELOC_ESP32S2ULP_GOT'?
927 | { $$ = BFD_RELOC_ESP32S2ULP_GOT17M4; }
| ^~~~~~~~~~~~~~~~~~~~~~
| BFD_RELOC_ESP32S2ULP_GOT
./config/esp32s2ulp-parse.y:927:18: note: each undeclared identifier is reported only once for each function it appears in
./config/esp32s2ulp-parse.y:929:18: error: 'BFD_RELOC_ESP32S2ULP_FUNCDESC_GOT17M4' undeclared (first use in this function); did you mean 'BFD_RELOC_BFIN_FUNCDESC_GOT17M4'?
929 | { $$ = BFD_RELOC_ESP32S2ULP_FUNCDESC_GOT17M4; }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| BFD_RELOC_BFIN_FUNCDESC_GOT17M4
gmake[5]: *** [Makefile:951: esp32s2ulp-parse.o] Error 1
configure
arguments:
--target=esp32s2ulp-elf --disable-doc --disable-gdb --disable-libdecnumber --disable-readline --disable-sim --prefix=/usr/local/binutils-esp32s2ulp --mandir=/usr/local/binutils-esp32s2ulp/share/man --disable-nls --disable-werror
other info:
typecast error in make process:
libbfd.h:268:4: error: cast between incompatible function types from ‘bfd_boolean (*)(bfd )’ {aka ‘int ()(bfd )’} to ‘bfd_boolean ()(bfd *, bfd )’ {aka ‘int ()(bfd *, bfd *)’} [-Werror=cast-function-type]
gcc --version
gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0
I had little problem with using unconditional jumps to the address from the register. Eventually I have checked the file *.ulp.lst
and carefully compared instructions against ulp implementation. And in my opinion (and my ESP32 agree, because it seems to work this way), the unconditional jump to the address from the register is translated incorrectly. For example jump r0
is translated to 00000080
which for me is clearly jump to immediate 0x0 so beginning of the program, the bit sel
is not set. Every similar instruction jump r1
, jump r2
, jump r3works the same way:
01000080,
02000080,
03000080. The lower/upper case does not matter so
JUMP R0is translated the same way that
jump r0` and so on.
I can't find any older releases of the toolchain. the MSYS2 homepage has a 2019-05-24 version. Is there any way I could get older versions?
WARNING: Toolchain version is not supported: esp32-2019r1
Expected to see version: crosstool-ng-1.22.0-80-g6c4433a
Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.
WARNING: Compiler version is not supported: 8.2.0
Expected to see version(s): 5.2.0
Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.
Couldn't find how to compile ulp asm file under Windows and then how to integrate with f.ex. esp-idf project. More documentation would be nice
Hi,
This is not an issue but a contribution. I have implemented the ULP disassembler in objdump.exe.
Three files modified:
opcodes/esp32ulp-dis.c (contains the disassembler)
opcodes/disassemble.c : to tell objdump that we handle ESP32ULP architecture
include/dis-asm.h : to enable ESP32ULP architecture
objdump reads ELF files and can be used on the the .elf file or intermediate .o files.
syntax : esp32ulp-elf-objdump.exe -d file
Enjoy !
disassemble.c: add following lines
#define ARCH_esp32ulp
in line 38
#ifdef ARCH_esp32ulp case bfd_arch_esp32ulp: disassemble = print_insn_esp32ulp; break; #endif
in line 246
dis-asm.h:
extern int print_insn_esp32ulp (bfd_vma, disassemble_info *);
in line 234
esp32-ulp.c:
/* ESPULP ELF support for BFD.
Copyright (c) 2016-2017 Espressif Systems (Shanghai) PTE LTD.
This file is part of BFD, the Binary File Descriptor library.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
/* Disassembler for ESP32 ULP processor
file to be put in folder opcodes/disassemble
don't forget to add:
opcodes/disassemble.c:
#define ARCH_esp32ulp
..
#ifdef ARCH_esp32ulp
case bfd_arch_esp32ulp:
disassemble = print_insn_esp32ulp;
break;
#endif
#include "sysdep.h"
#include "dis-asm.h"
#include "floatformat.h"
#include "libiberty.h"
#include "opintl.h"
typedef struct
{
unsigned int operand: 28;
unsigned int opcode : 4;
} esp32ulp_inst;
static void disasm_esp32ulp_instruction(esp32ulp_inst inst, unsigned int addr);
static void unknown(void);
static void regwr(unsigned int operand);
static void regrd(unsigned int operand);
static void i2c(unsigned int operand);
static void wait(unsigned int operand);
static void adc(unsigned int operand);
static void store(unsigned int operand);
static void operation(unsigned int operand);
static void operation_reg(unsigned int operand);
static void operation_imm(unsigned int operand);
static void operation_stage(unsigned int operand);
static void jmp(unsigned int operand, unsigned int address);
static void jump(unsigned int operand, unsigned int address);
static void jumpr(unsigned int operand, unsigned int address);
static void jumps(unsigned int operand, unsigned int address);
static void wakesleep(unsigned int operand);
static void tsens(unsigned int operand);
static void halt(unsigned int operand);
static void load(unsigned int operand);
#define ESP32ULP_INSTR_SIZE 4
#define INST_TO_ADDR(inst) ((inst)*ESP32ULP_INSTR_SIZE)
#define ADDR_TO_INST(addr) ((addr)/ESP32ULP_INSTR_SIZE)
#define COPY(a,b) memcpy(&a, &b, sizeof(a));
/* Maximum length of an instruction. */
#define MAXLEN ESP32ULP_INSTR_SIZE
struct private
{
/* Points to first byte not fetched. */
bfd_byte *max_fetched;
bfd_byte the_buffer[MAXLEN];
bfd_vma insn_start;
};
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
to ADDR (exclusive) are valid. Returns 1 for success, 0 on error. */
#define FETCH_DATA(info, addr)
((addr) <= ((struct private *) (info->private_data))->max_fetched
? 1 : fetch_data ((info), (addr)))
static int
fetch_data (struct disassemble_info *info, bfd_byte *addr)
{
int status;
struct private *priv = (struct private *)info->private_data;
bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
status = (*info->read_memory_func) (start,
priv->max_fetched,
addr - priv->max_fetched,
info);
if (status != 0)
{
(*info->memory_error_func) (status, start, info);
return 0;
}
else
priv->max_fetched = addr;
return 1;
}
struct disassemble_info* dinfo ;
int
print_insn_esp32ulp (bfd_vma memaddr, disassemble_info *info)
{
dinfo = info;
struct private priv;
bfd_byte *buffer = priv.the_buffer;
dinfo->private_data = & priv;
priv.max_fetched = priv.the_buffer;
priv.insn_start = memaddr;
FETCH_DATA (dinfo, buffer + ESP32ULP_INSTR_SIZE);
esp32ulp_inst inst;
COPY(inst, *buffer);
disasm_esp32ulp_instruction(inst, (unsigned int)memaddr);
return ESP32ULP_INSTR_SIZE;
}
static void disasm_esp32ulp_instruction(esp32ulp_inst inst, unsigned int addr)
{
// dinfo->fprintf_func(dinfo->stream, "%04x %04x %01x%07x ", ADDR_TO_INST(addr), addr, inst.opcode, inst.operand);
unsigned int operand = inst.operand;
switch(inst.opcode)
{
case 1: regwr(operand); break;
case 2: regrd(operand); break;
case 3: i2c(operand); break;
case 4: wait(operand); break;
case 5: adc(operand); break;
case 6: store(operand); break;
case 7: operation(operand); break;
case 8: jmp(operand, addr); break;
case 9: wakesleep(operand); break;
case 10: tsens(operand); break;
case 11: halt(operand); break;
case 13: load(operand); break;
default: unknown(); break;
}
}
static void unknown(void)
{
dinfo->fprintf_func(dinfo->stream, "????");
}
// from soc.h
#define DR_REG_RTCCNTL_BASE 0x3ff48000
#define DR_REG_RTCIO_BASE 0x3ff48400
#define DR_REG_SENS_BASE 0x3ff48800
#define DR_REG_RTC_I2C_BASE 0x3ff48C00
typedef struct {
unsigned int addr:10;
unsigned int data:8;
unsigned int low: 5;
unsigned int high: 5;
} fregwr;
static void regwr(unsigned int operand)
{
fregwr op;
COPY(op, operand);
unsigned int addr = INST_TO_ADDR(op.addr)+DR_REG_RTCCNTL_BASE;
unsigned int base;
unsigned int offset;
char* pstr;
if (addr>=DR_REG_RTC_I2C_BASE) { base = DR_REG_RTC_I2C_BASE; pstr = "RTC_I2C_BASE"; }
else if (addr>=DR_REG_SENS_BASE) { base = DR_REG_SENS_BASE; pstr = "SENS_BASE"; }
else if (addr>=DR_REG_RTCIO_BASE) { base = DR_REG_RTCIO_BASE; pstr = "RTCIO_BASE"; }
else { base = DR_REG_RTCCNTL_BASE ;pstr = "RTCCNTL_BASE"; }
offset = addr - base;
dinfo->fprintf_func(dinfo->stream, "REG_WR ");
dinfo->fprintf_func(dinfo->stream, "0x%x+0x%x, %d, %d, 0x%x", base, offset, op.high, op.low, op.data );
dinfo->fprintf_func(dinfo->stream, "\t // REG(%s+0x%x)[%d:%d]=0x%x", pstr, offset, op.high, op.low, op.data);
}
typedef struct {
unsigned int addr:10;
unsigned int dummy:8;
unsigned int low: 5;
unsigned int high: 5;
} fregrd;
static void regrd(unsigned int operand)
{
fregrd op;
COPY(op, operand);
unsigned int addr = INST_TO_ADDR(op.addr)+DR_REG_RTCCNTL_BASE;
unsigned int base;
unsigned int offset;
char* pstr;
if (addr>=DR_REG_RTC_I2C_BASE) { base = DR_REG_RTC_I2C_BASE; pstr = "RTC_I2C_BASE"; }
else if (addr>=DR_REG_SENS_BASE) { base = DR_REG_SENS_BASE; pstr = "SENS_BASE"; }
else if (addr>=DR_REG_RTCIO_BASE) { base = DR_REG_RTCIO_BASE; pstr = "RTCIO_BASE"; }
else { base = DR_REG_RTCCNTL_BASE ;pstr = "RTCCNTL_BASE"; }
offset = addr - base;
dinfo->fprintf_func(dinfo->stream, "REG_RD ");
dinfo->fprintf_func(dinfo->stream, "0x%x+0x%x, %d, %d", base, offset, op.high, op.low);
dinfo->fprintf_func(dinfo->stream, "\t\t // REG(%s+0x%x)[%d:%d]=0x%x", pstr, offset, op.high, op.low);
}
typedef struct {
unsigned int subaddr:8;
unsigned int data:8;
unsigned int low: 3;
unsigned int high: 3;
unsigned int sel: 4;
unsigned int dummy:1;
unsigned int rw: 1;
} fi2c;
static void i2c(unsigned int operand)
{
fi2c op;
COPY(op, operand);
if (op.rw == 0)
dinfo->fprintf_func(dinfo->stream, "I2C_RD 0x%x, %d, %d, %d \t\t // I2C_READ SLAVE_ADDR 0x%x, REG%d [%d:%d]", op.subaddr, op.high, op.low, op.sel, op.subaddr, op.sel, op.low, op.high);
else
dinfo->fprintf_func(dinfo->stream, "I2C_WR 0x%x, 0x%x, %d, %d, %d \t // I2C_WRITE SLAVE_ADDR 0x%x, REG%d [%d:%d]=%x", op.subaddr, op.data, op.high, op.low, op.sel, op.subaddr, op.sel, op.low, op.high, op.data);
}
typedef struct {
unsigned int cycles:16;
unsigned int dummy:8;
} fwait;
static void wait(unsigned int operand)
{
fwait op;
COPY(op, operand);
if (op.cycles == 0)
dinfo->fprintf_func(dinfo->stream, "NOP", op.cycles);
else
dinfo->fprintf_func(dinfo->stream, "WAIT %d", op.cycles);
}
typedef struct {
unsigned int Rdst:2;
unsigned int Sarmux:4;
unsigned int sel:1;
unsigned int dummy:21;
} fadc;
static void adc(unsigned int operand)
{
fadc op;
COPY(op, operand);
dinfo->fprintf_func(dinfo->stream, "ADC R%d, %d, %d\t\t\t\t\t // R%d=ADC%d pad%d", op.Rdst, op.Sarmux, op.sel, op.Rdst, op.Sarmux+1, op.sel+1);
}
typedef struct
{
unsigned int operand:25;
unsigned int choice: 3;
} fop;
typedef struct
{
unsigned int Rdst: 2;
unsigned int Rsrc1: 2;
unsigned int Rsrc2: 2;
unsigned int dummy:15;
unsigned int sel: 4;
unsigned int choice:3; // 0
} fop_reg;
typedef struct
{
unsigned int Rdst: 2;
unsigned int Rsrc1: 2;
unsigned int imm: 16;
unsigned int dummy1:1;
unsigned int sel: 4;
unsigned int choice:3; // 1
} fop_imm;
typedef struct
{
unsigned int dummy: 4;
unsigned int imm: 8;
unsigned int dummy2:9;
unsigned int sel: 4;
unsigned int choice:3; // 2
} fop_stage;
static void operation(unsigned int operand)
{
fop op;
COPY(op, operand);
switch(op.choice)
{
case 0: operation_reg(operand); break;
case 1: operation_imm(operand); break;
case 2: operation_stage(operand); break;
default: dinfo->fprintf_func(dinfo->stream, "ALU ???"); break;
}
}
static void operation_reg(unsigned int operand)
{
fop_reg opr;
COPY(opr, operand);
switch(opr.sel)
{
case 0: dinfo->fprintf_func(dinfo->stream, "ADD R%d, R%d, R%d \t\t\t\t\t // R%d = R%d + R%d", opr.Rdst, opr.Rsrc1, opr.Rsrc2, opr.Rdst, opr.Rsrc1, opr.Rsrc2); break;
case 1: dinfo->fprintf_func(dinfo->stream, "SUB R%d, R%d, R%d \t\t\t\t\t // R%d = R%d - R%d", opr.Rdst, opr.Rsrc1, opr.Rsrc2, opr.Rdst, opr.Rsrc1, opr.Rsrc2); break;
case 2: dinfo->fprintf_func(dinfo->stream, "AND R%d, R%d, R%d \t\t\t\t\t // R%d = R%d & R%d", opr.Rdst, opr.Rsrc1, opr.Rsrc2, opr.Rdst, opr.Rsrc1, opr.Rsrc2); break;
case 3: dinfo->fprintf_func(dinfo->stream, "OR R%d, R%d, R%d \t\t\t\t\t // R%d = R%d | R%d", opr.Rdst, opr.Rsrc1, opr.Rsrc2, opr.Rdst, opr.Rsrc1, opr.Rsrc2); break;
case 4: dinfo->fprintf_func(dinfo->stream, "MOVE R%d, R%d \t\t\t\t\t // R%d = R%d", opr.Rdst, opr.Rsrc1, opr.Rdst, opr.Rsrc1); break;
case 5: dinfo->fprintf_func(dinfo->stream, "LSH R%d, R%d, R%d \t\t\t\t\t // R%d = R%d<<R%d", opr.Rdst, opr.Rsrc1, opr.Rsrc2, opr.Rdst, opr.Rsrc1, opr.Rsrc2); break;
case 6: dinfo->fprintf_func(dinfo->stream, "RSH R%d, R%d, R%d \t\t\t\t\t // R%d = R%d>>R%d", opr.Rdst, opr.Rsrc1, opr.Rsrc2, opr.Rdst, opr.Rsrc1, opr.Rsrc2); break;
default: dinfo->fprintf_func(dinfo->stream, "ALU ????"); break;
}
}
static void operation_imm(unsigned int operand)
{
fop_imm opi;
COPY(opi, operand);
switch(opi.sel)
{
case 0: dinfo->fprintf_func(dinfo->stream, "ADD R%d, R%d, %d \t\t\t\t\t // R%d = R%d + %d", opi.Rdst, opi.Rsrc1, opi.imm, opi.Rdst, opi.Rsrc1, opi.imm); break;
case 1: dinfo->fprintf_func(dinfo->stream, "SUB R%d, R%d, %d \t\t\t\t\t // R%d = R%d - %d", opi.Rdst, opi.Rsrc1, opi.imm, opi.Rdst, opi.Rsrc1, opi.imm); break;
case 2: dinfo->fprintf_func(dinfo->stream, "AND R%d, R%d, 0x%04x \t\t\t\t // R%d = R%d & 0x%x", opi.Rdst, opi.Rsrc1, opi.imm, opi.Rdst, opi.Rsrc1, opi.imm); break;
case 3: dinfo->fprintf_func(dinfo->stream, "OR R%d, R%d, 0x%04x \t\t\t\t // R%d = R%d | 0x%x", opi.Rdst, opi.Rsrc1, opi.imm, opi.Rdst, opi.Rsrc1, opi.imm); break;
case 4: dinfo->fprintf_func(dinfo->stream, "MOVE R%d, %d \t\t\t\t\t // R%d = %d \t\t\t (can also be pointer to address 0x%04x)", opi.Rdst, opi.imm, opi.Rdst, opi.imm, INST_TO_ADDR(opi.imm)); break;
case 5: dinfo->fprintf_func(dinfo->stream, "LSH R%d, R%d, %d \t\t\t\t\t // R%d = R%d<<%d", opi.Rdst, opi.Rsrc1, opi.imm, opi.Rdst, opi.Rsrc1, opi.imm); break;
case 6: dinfo->fprintf_func(dinfo->stream, "RSH R%d, R%d, %d \t\t\t\t\t // R%d = R%d>>%d", opi.Rdst, opi.Rsrc1, opi.imm, opi.Rdst, opi.Rsrc1, opi.imm); break;
default: dinfo->fprintf_func(dinfo->stream, "ALU ????"); break;
}
}
static void operation_stage(unsigned int operand)
{
fop_stage ops;
COPY(ops, operand);
switch(ops.sel)
{
case 0: dinfo->fprintf_func(dinfo->stream, "STAGE_INC %d", ops.imm); break;
case 1: dinfo->fprintf_func(dinfo->stream, "STAGE_DEC %d", ops.imm); break;
case 2: dinfo->fprintf_func(dinfo->stream, "STAGE_RST"); break;
default: dinfo->fprintf_func(dinfo->stream, "ALU ????"); break;
}
}
typedef struct
{
unsigned int operand:25;
unsigned int choice:3;
} fjmp;
typedef struct
{
unsigned int rdest:2;
unsigned int addr:11;
unsigned int dummy:8;
unsigned int sel: 1;
unsigned int type: 2;
unsigned int choice:3; // 0
} fjump;
typedef struct
{
unsigned int thres:16;
unsigned int cond: 1;
unsigned int step: 8;
unsigned int choice:3; // 1
} fjumpr;
typedef struct
{
unsigned int thres: 8;
unsigned int dummy: 7;
unsigned int cond: 2;
unsigned int step: 8;
unsigned int choice:3; // 2
} fjumps;
static void jmp(unsigned int operand, unsigned int address)
{
fjmp op;
COPY(op, operand);
switch(op.choice)
{
case 0: jump(operand, address); break;
case 1: jumpr(operand, address); break;
case 2: jumps(operand, address); break;
default: dinfo->fprintf_func(dinfo->stream, "JUMP????"); break;
}
}
static void jump(unsigned int operand, unsigned int address)
{
fjump op;
COPY(op, operand);
address = address; // fake instruction to astatic void compiler warning (unused parameter)
dinfo->fprintf_func(dinfo->stream, "JUMP ");
if (op.sel==0)
dinfo->fprintf_func(dinfo->stream, "0x%04x", INST_TO_ADDR(op.addr));
else
dinfo->fprintf_func(dinfo->stream, "R%d", op.rdest);
switch(op.type)
{
case 1: dinfo->fprintf_func(dinfo->stream, ",EQ \t\t\t\t\t // if operation==0 THEN JUMP"); break;
case 2: dinfo->fprintf_func(dinfo->stream, ",OV \t\t\t\t\t // if overflow THEN JUMP"); break;
}
}
static void jumpr(unsigned int operand, unsigned int address)
{
fjumpr op;
COPY(op, operand);
unsigned int relative = INST_TO_ADDR(op.step&0x7F);
int sign = (op.step&0x80 ? -1 : +1);
dinfo->fprintf_func(dinfo->stream, "JUMPR ");
dinfo->fprintf_func(dinfo->stream, "0x%04x, %d, %s", address+sign*relative, op.thres, (op.cond==0?"lt":"ge") );
dinfo->fprintf_func(dinfo->stream, "\t\t\t\t\t // IF R0%s%d THEN JUMP TO [0x%04x%c0x%02x]", (op.cond==0?"<":">="), op.thres, address, (sign>0?'+':'-'), relative);
}
static void jumps(unsigned int operand, unsigned int address)
{
fjumps op;
COPY(op, operand);
unsigned int relative = INST_TO_ADDR(op.step&0x7F);
int sign = (op.step&0x80 ? -1 : +1);
dinfo->fprintf_func(dinfo->stream, "JUMPS ");
dinfo->fprintf_func(dinfo->stream, "0x%04x, ", address+sign*relative);
switch(op.cond)
{
case 0: dinfo->fprintf_func(dinfo->stream, "%d, lt", op.thres); break;
case 1: dinfo->fprintf_func(dinfo->stream, "%d, gt", op.thres); break;
default: dinfo->fprintf_func(dinfo->stream, "%d, eq", op.thres); break;
}
dinfo->fprintf_func(dinfo->stream, "\t\t\t\t\t // ");
switch(op.cond)
{
case 0: dinfo->fprintf_func(dinfo->stream, "IF COUNT<%d ", op.thres); break;
case 1: dinfo->fprintf_func(dinfo->stream, "IF COUNT>%d ", op.thres); break;
default: dinfo->fprintf_func(dinfo->stream, "IF COUNT==%d ", op.thres); break;
}
dinfo->fprintf_func(dinfo->stream,"THEN JUMP TO [0x%04x%c0x%02x]", address, (sign>0?'+':'-'), relative);
}
typedef struct {
unsigned int reg:4;
unsigned int dummy:19;
unsigned int wakeorsleep:1;
} fwakesleep;
static void wakesleep(unsigned int operand)
{
fwakesleep op;
COPY(op, operand);
if (op.wakeorsleep==0)
dinfo->fprintf_func(dinfo->stream, "WAKE");
else
dinfo->fprintf_func(dinfo->stream, "SLEEP R%d", op.reg);
}
typedef struct {
unsigned int Rdst:2;
unsigned int delay:14;
unsigned int dummy:12;
} ftsens;
static void tsens(unsigned int operand)
{
ftsens op;
COPY(op, operand);
dinfo->fprintf_func(dinfo->stream, "TSENS R%d, %d\t\t\t\t\t // delay=%d", op.Rdst, op.delay, op.delay);
}
static void halt(unsigned int operand)
{
operand = operand; // fake instruction to astatic void compiler warning (unused parameter)
dinfo->fprintf_func(dinfo->stream, "HALT");
}
typedef struct {
unsigned int Rdst:2;
unsigned int Rsrc:2;
unsigned int dummy:6;
unsigned int offset:11;
unsigned int dummy2: 7;
} fload;
static void load(unsigned int operand)
{
fload op;
COPY(op, operand);
dinfo->fprintf_func(dinfo->stream, "LD R%d, R%d, %d \t\t\t\t\t // R%d = MEM[R%d+%d]", op.Rdst, op.Rsrc, INST_TO_ADDR(op.offset), op.Rdst, op.Rsrc, INST_TO_ADDR(op.offset));
}
typedef struct {
unsigned int Rdst:2;
unsigned int Rsrc:2;
unsigned int null:6;
unsigned int offset:11;
unsigned int null2: 4;
unsigned int cent: 3;
} fstore;
static void store(unsigned int operand)
{
fstore op;
COPY(op, operand);
dinfo->fprintf_func(dinfo->stream, "ST R%d, R%d, %d \t\t\t\t\t // MEM[R%d+%d] = R%d", op.Rdst, op.Rsrc, INST_TO_ADDR(op.offset), op.Rsrc, INST_TO_ADDR(op.offset), op.Rdst);
}
Hi, there's a build for arm64?
I have issue on my rpi installing esp-idf ->I know is not supported :-)
gcc -c -DHAVE_CONFIG_H -g -O2 -D__USE_MINGW_ACCESS -I. -I./../include -W -Wall -Wwrite-strings -Wc++-compat -Wstrict-prototypes -Wshadow=local -pedantic -D_GNU_SOURCE ./pex-win32.c -o pex-win32.o
./pex-win32.c: In function ‘pex_win32_open_read’:
./pex-win32.c:121:10: warning: implicit declaration of function ‘_open’; did you mean ‘open’? [-Wimplicit-function-declaration]
121 | return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
| ^~~~~
| open
./pex-win32.c:121:23: error: ‘_O_RDONLY’ undeclared (first use in this function); did you mean ‘O_RDONLY’?
121 | return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
| ^~~~~~~~~
| O_RDONLY
./pex-win32.c:121:23: note: each undeclared identifier is reported only once for each function it appears in
./pex-win32.c:121:45: error: ‘_O_BINARY’ undeclared (first use in this function); did you mean ‘O_BINARY’?
121 | return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
| ^~~~~~~~~
| O_BINARY
./pex-win32.c:121:57: error: ‘_O_TEXT’ undeclared (first use in this function); did you mean ‘O_TEXT’?
121 | return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
| ^~~~~~~
| O_TEXT
./pex-win32.c: In function ‘pex_win32_open_write’:
./pex-win32.c:135:4: error: ‘_O_WRONLY’ undeclared (first use in this function); did you mean ‘O_WRONLY’?
135 | (_O_WRONLY | _O_CREAT | _O_TRUNC
| ^~~~~~~~~
| O_WRONLY
./pex-win32.c:135:16: error: ‘_O_CREAT’ undeclared (first use in this function); did you mean ‘O_CREAT’?
135 | (_O_WRONLY | _O_CREAT | _O_TRUNC
| ^~~~~~~~
| O_CREAT
./pex-win32.c:135:27: error: ‘_O_TRUNC’ undeclared (first use in this function); did you mean ‘O_TRUNC’?
135 | (_O_WRONLY | _O_CREAT | _O_TRUNC
| ^~~~~~~~
| O_TRUNC
./pex-win32.c:136:16: error: ‘_O_BINARY’ undeclared (first use in this function); did you mean ‘O_BINARY’?
136 | | (binary ? _O_BINARY : _O_TEXT)),
| ^~~~~~~~~
| O_BINARY
./pex-win32.c:136:28: error: ‘_O_TEXT’ undeclared (first use in this function); did you mean ‘O_TEXT’?
136 | | (binary ? _O_BINARY : _O_TEXT)),
| ^~~~~~~
| O_TEXT
./pex-win32.c:137:3: error: ‘_S_IREAD’ undeclared (first use in this function); did you mean ‘S_IREAD’?
137 | _S_IREAD | _S_IWRITE);
| ^~~~~~~~
| S_IREAD
./pex-win32.c:137:14: error: ‘_S_IWRITE’ undeclared (first use in this function); did you mean ‘S_IWRITE’?
137 | _S_IREAD | _S_IWRITE);
| ^~~~~~~~~
| S_IWRITE
./pex-win32.c: In function ‘pex_win32_close’:
./pex-win32.c:145:10: warning: implicit declaration of function ‘_close’; did you mean ‘close’? [-Wimplicit-function-declaration]
145 | return _close (fd);
| ^~~~~~
| close
./pex-win32.c: In function ‘win32_spawn’:
./pex-win32.c:652:10: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
652 | return (pid_t) pi->hProcess;
| ^
./pex-win32.c: In function ‘spawn_script’:
./pex-win32.c:674:31: error: ‘_O_RDONLY’ undeclared (first use in this function); did you mean ‘O_RDONLY’?
674 | int fd = _open (executable, _O_RDONLY);
| ^~~~~~~~~
| O_RDONLY
./pex-win32.c:681:17: warning: implicit declaration of function ‘_read’; did you mean ‘read’? [-Wimplicit-function-declaration]
681 | int len = _read (fd, buf, sizeof (buf) - 1);
| ^~~~~
| read
./pex-win32.c: In function ‘pex_win32_exec_child’:
./pex-win32.c:779:8: warning: implicit declaration of function ‘_dup’; did you mean ‘dup’? [-Wimplicit-function-declaration]
779 | in = _dup (orig_in);
| ^~~~
| dup
./pex-win32.c: In function ‘pex_win32_wait’:
./pex-win32.c:911:7: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
911 | h = (HANDLE) pid;
| ^
./pex-win32.c: In function ‘pex_win32_pipe’:
./pex-win32.c:943:10: warning: implicit declaration of function ‘_pipe’; did you mean ‘pipe’? [-Wimplicit-function-declaration]
943 | return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
| ^~~~~
| pipe
./pex-win32.c:943:35: error: ‘_O_BINARY’ undeclared (first use in this function); did you mean ‘O_BINARY’?
943 | return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
| ^~~~~~~~~
| O_BINARY
./pex-win32.c:943:47: error: ‘_O_TEXT’ undeclared (first use in this function); did you mean ‘O_TEXT’?
943 | return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
| ^~~~~~~
| O_TEXT
./pex-win32.c:943:58: error: ‘_O_NOINHERIT’ undeclared (first use in this function)
943 | return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT);
| ^~~~~~~~~~~~
./pex-win32.c:944:1: warning: control reaches end of non-void function [-Wreturn-type]
944 | }
| ^
./pex-win32.c: In function ‘pex_win32_open_write’:
./pex-win32.c:138:1: warning: control reaches end of non-void function [-Wreturn-type]
138 | }
| ^
./pex-win32.c: In function ‘pex_win32_open_read’:
./pex-win32.c:122:1: warning: control reaches end of non-void function [-Wreturn-type]
122 | }
| ^
make[2]: *** [Makefile:1121: pex-win32.o] Error 1
make[2]: Leaving directory '/home/jacob/esp/esp-idf/binutils-esp32ulp/libiberty'
make[1]: *** [Makefile:8376: all-libiberty] Error 2
make[1]: Leaving directory '/home/jacob/esp/esp-idf/binutils-esp32ulp'
make: *** [Makefile:858: all] Error 2
$ gcc --version
gcc (GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Building on windows 10 doing:
CC=gcc ./configure
make
Using version 2.35_20220830 from esp-idf 5.1, MOVE Rx, Ry
is apparently assembled as:
(MSB)
1110000100000000000000000yyyyxx
i.e. Rsrc2 is equal to Rsrc1, as if it was MOVE Rx, Rx, Ry
, while TRM doesn't specify Rsrc2 for MOVE instruction.
Shouldn't it be:
(MSB)
111000010000000000000000000yyxx
instead?
Is it intended behavior?
Apperance of an error:
coprocessor.ulp.pS:276: Error: rel too far BFD_JUMPR_STEP
do not depend on relative position of instructions but for some reason on absolute position of instruction. Above error appears in my code:
jumpr quit, 256, ge
halt
quit: wake
when those lines are very far away e.g.
276 ???? 00010182 jumpr quit, 256, ge
277 ???? 000000B0 halt
278 quit:
279 ???? 01000090 wake
but won't appear if those lines are at the beginning of code. The problem also affects jumps
instruction.
EDIT: I have found that if label which i selected to jump into is going to be over 0x0200
address in resultant code, such error appears, so I can generate code:
186 01f8 00000040 nop
187 test1:
188 01fc 00010182 jumpr test1, 256, ge
189 test2:
190 0200 00000040 nop
when i try to jump to test1
, but only when I try to jump into test2
I get:
coprocessor.ulp.pS: Assembler messages:
coprocessor.ulp.pS:188: Error: rel too far BFD_JUMPR_STEP
186 ???? 00000040 nop
187 test1:
188 ???? 00010182 jumpr test2, 256, ge
189 test2:
190 ???? 00000040 nop
the error is thrown by function md_apply_fix
in file gas/config/tc-esp32ulp.c
, but the condition checked there seems legit:
case BFD_RELOC_ESP32ULP_JUMPR_STEP:
if ((value > 0x1FF) || (value < -0x1ff))
as_bad_where(fixP->fx_file, fixP->fx_line, _("rel too far BFD_JUMPR_STEP"));
because it assume (i hope) that value
is relative offset of jump, but for some reason in this case it is not, and it happened to be absolute offset of jump, I am not sure from where this function is called, because I am not so familiar with binutils source code.
I get the following errors:
[myfilename.S] Relocation 2 not handled in gas. Contact support.
[myfilename.S]: Assembler messages:
[myfilename.S:linenumber]: Error: reloc 2 not supported by object file format
When trying to use the address of a label as a number. This code throws the error:
.data
important:
.long 0
important_addr:
.long important
The error gets thrown here. I believe that BFD_RELOC_32 needs to be added to the switch statement. This is how another cpu handles it. I tried to add it but couldn't get the project to compile.
For JUMP instructions using an absolute/constant symbol as an argument, binutils-esp32ulp produces a different output depending on whether that symbol is exported with .global
or not. This appears to be a bug.
The following example code can be used to reproduce the issue:
.set const, 12
.global const
entry:
jump const
Assemble and link the example as follows:
base=global_bug # given the source is called "global_bug.s"
esp32ulp-elf-as -o ${base}.o ${base}.s
esp32ulp-elf-ld -T esp32.ulp.ld -o ${base}.elf ${base}.o
esp32ulp-elf-objcopy -O binary ${base}.elf ${base}.bin
Inspect the binary output as follows:
xxd global_bug.bin
When building the example code as is, the binary result is:
0000000: 756c 7000 0c00 0400 0000 0000 3000 0080 ulp.........0...
However, when commenting out (or removing) the line .global const
- in other words const
is NOT exported, then the binary result becomes:
0000000: 756c 7000 0c00 0400 0000 0000 0c00 0080 ulp.............
Notice the last (JUMP) instruction is different: 3000 0080
vs 0c00 0080
.
Decoding those two, I get
# global
3000 0080:
dreg = 0 # 0
addr = 12 # Target PC
unused = 0 # Unused
reg = 0 # Immediate mode
type = 0 # BX_JUMP_TYPE_DIRECT
opcode = 8 # OPCODE_BRANCH
sub_opcode = 0 # SUB_OPCODE_BX
# NOT global
0c00 0080:
dreg = 0 # 0
addr = 3 # Target PC
unused = 0 # Unused
reg = 0 # Immediate mode
type = 0 # BX_JUMP_TYPE_DIRECT
opcode = 8 # OPCODE_BRANCH
sub_opcode = 0 # SUB_OPCODE_BX
Notice how the addr
field is different. In fact in the second case (not global) the symbol value has been divided by 4.
I assume this is related to address translation from bytes to words, however the behaviour should at least be consistent, no matter whether a symbol is marked global or not.
I would expect the correct behaviour to be, that the JUMP instruction uses the symbol value as is (ie. addr == 12
- not divided by 4).
This would match how the ESP32 ULP coprocessor instruction set documentation describes that arguments, which are constants rather than labels, are used without conversion.
Furthermore, the JUMPR
and JUMPS
instructions behave that way too (i.e. they use those symbol values without conversion), irrespective of whether the symbol is marked global or not.
I can run the ulp example for ESP32 IDF just fine.
While compiling on a Raspberry Pi 3 the build fails fast:
$ uname -a
Linux pi32 4.14.50-v7+ #1122 SMP Tue Jun 19 12:26:26 BST 2018 armv7l GNU/Linux
$ cd ~/esp/esp-idf/examples/system/ulp
$ make flash
CPP main/ulp/pulse_cnt.S
ULP_AS build/main/pulse_cnt.ulp.o
pulse_cnt.ulp.pS: Assembler messages:
pulse_cnt.ulp.pS:24: Error: Register address out of range. Must be 0..255, or in range of 0x3ff48000 .. 0x3ff48400.
/home/barde/esp/esp-idf/components/ulp/component_ulp_common.mk:40: recipe for target 'pulse_cnt.ulp.o' failed
make[1]: *** [pulse_cnt.ulp.o] Error 1
/home/barde/esp/esp-idf/make/project.mk:468: recipe for target 'component-main-build' failed
make: *** [component-main-build] Error 2```
This issue relates to how relative offsets specified as immediate values are handled after my recent PR (#18).
After my recent PR (#18) was merged - thank you btw - I noticed that we can now have "negative 0" in the JUMP{R,S} instructions (i.e. offset=0 and sign=1).
This happens when the offset in bytes is between -1, -2 or -3 and is converted to words. During the conversion (divide by 4) the result is effectively "rounded" to 0 (correct), but the sign bit in the jump instruction is still set as negative.
I assume this does not matter to the ULP, but it felt imperfect to me anyway.
Digging deeper I noticed that while my PR handles immediate offsets exactly how I would expect (except for the -0 possibility), I got to this behaviour "by accident".
The accident/mistake was to not wrap stp
in brackets in the I_JUMP_REL{R,S}
macros (see here). When the macro gets passed step_val>>2
(see here), -stp
in the macro actually expands to -step_val>>2
and the unary minus has higher operator precedence than the shift-right (i.e. it is evaluated before the shift-right). This results in a positive number being right-shifted, instead of a negative.
The matters because with negative numbers, shift-right and division behave differently (apparently right-shift on negative numbers is not even well defined, rather it's "implementation specific"). This all raises the question of "how should it work"?
To show the issue (difference between divide and shift):
Divide Shift-Right
5 / 4 = 1 5 >> 2 = 1
4 / 4 = 1 4 >> 2 = 1
3 / 4 = 0 3 >> 2 = 0
1 / 4 = 0 1 >> 2 = 0
-1 / 1 = 0 -1 >> 2 = -1 #different
-3 / 4 = 0 -3 >> 2 = -1 #different
-4 / 4 = -1 -4 >> 2 = -1
-5 / 4 = -1 -5 >> 2 = -2 #different
The current behaviour matches my expectation, which is based on how the ULP interprets the offset field. The ULP uses bit 7 from the offset field to determine direction (sign), while bits 0-6 are the absolute offset, so I would expect positive and negative offsets to result in the same absolute offset (magnitude), with only the sign bit being different.
So, how should it work? I notice that all places in the code, which convert offsets from bytes to words, use the shift-right approach, rather than divide by 4. The documentation describes what the assembler does as "converts bytes to words", so it appears the intent would be to "divide by 4" rather than to "shift something".
I did find some other code, which explicitly checks for "multiple of 4" (here. That could be another solution: simply disallow values that are not multiples of 4.
As I write and test this, I realise that this all might not be very important. When a negative input is exactly divisible by 4, the result will be identical (correct) for both the shift-right and division approach. It's only for not-a-multiple-of-4 values, where the "rounding" is done differently. And since only word-aligned addresses are actually addressable, real programs should only use offsets that are actually multiples of 4. So perhaps leaving this case entirely undefined is perfectly fine? I dislike undefined though.
So, to summarise: This is mostly about understanding (deciding) how the assembler should behave with negative offsets. The options I see:
>>2
to /4
operations. This should express the intent better, and the compiler can optimise these as necessary/possible.esp32ulp_cmd_jump_relr_esp32
), to only accept step_val
inputs that are multiples of 4.stp
correctly in brackets, so that negative numbers are right-shifted (resulting in a slightly different result compared to divide-by-4, as shown above). Perhaps there is a reason why this would be more correct?@dmitry1945 - since you merged my recent PR (#18), perhaps you have this freshest in memory and I could ask you to comment? I am happy to create another PR to adjust the behaviour if it should be different to what it is now.
Pre-compiled releases for 32b systems should be also released alongside 64b
Hi
I have discovered that esp32ulp-elf-as does not generate correct relocation information in the .o file in some particular cases.
Eg:
.macro test label: wait 1 jump label .endm .text test
In such a situation, as will indicate that the relocation to be applied to jump label has to be applied on the first instruction (ie. wait 1).
This is what esp32ulp-elf-readelf confirms:
Offset Info Type Val.-sym Noms-symb + Addenda
**00000000** 00000101 R_ESP32ULP_RIMM16 00000000 .text + 0
Offset is 0 instead of 4 (bytes) so esp32ulp-elf-ld will apply the relocation to wait and not to jump and will corrupt the code.
The same kind of issue will always happen in macro if the relocation is not the first instruction of the macro. Same will apply if we gather several instructions on the same line (a macro is handled this way):
loop: wait 1; jump loop;
After many hours of investigation I have found that the problem is located in tc-esp32ulp.c in the function md_assemble().
It always applies the relocation to the offset 0 of the current fragment of code (a fragment of code is a set of instructions ; in this implementation a fragment of code is all the instructions that appear on the same line, meaning that a new fragment is generated on every new line). Of course this does not work with macro's neither since when we handle the instructions inside of the macro, we stay on the same line of the source code.
Faulty code:
if (insn->reloc && insn->exp->symbol)
{
size = 4;
//DEBUG_TRACE("insn->reloc && insn->exp->symbol BFD_ARELOC_ESP32ULP_PUSH size =%i, insn->exp->value=%i, insn->pcrel=%i, insn->reloc=%i\n", size, (unsigned int)insn->exp->value, insn->pcrel, insn->reloc);
//char *prev_toP = toP - 2;
//fix_new(frag_now, (prev_toP - frag_now->fr_literal), size, insn->exp->symbol, insn->exp->value, insn->pcrel, insn->reloc);
// were - shift from current word...
fix_new(frag_now, 0, size, insn->exp->symbol, insn->exp->value, insn->pcrel, insn->reloc); // THE MISTAKE IS HERE, 2ND PARAMETER SHALL NOT BE 0
}
else
{
//DEBUG_TRACE("md_number_to_chars insn->value =%08x\n", (unsigned int)insn->value);
//md_number_to_chars(toP, insn->value, 2);
//toP += 2;
// TODO:DYA - this is main changes for 32 bit words!!!!! changes stop work
md_number_to_chars(toP, insn->value, 4); // THIS IS THE SECOND BUG, WE SHALL ONLY DUMP THE FIRST 4 BYTES OUTPUT BY PARSER (first insn, not the ones which follow)
toP += 4;
}
Moreover there is another bug (which does not cause problem in practice) : the loop will dump to the instruction buffer all what is coming out of the parser, whereas we shall only put the 4 bytes of the instruction. In theory it causes a buffer overflow but by chance will not corrupt anything.
I propose the following implementation of the md_assemble:
void
md_assemble(char *line)
{
char *toP ;
int insn_size;
size_t len;
static size_t buffer_len = 0;
static char *current_inputline;
parse_state state;
len = strlen(line);
if (len + 2 > buffer_len)
{
buffer_len = len + 40;
current_inputline = XRESIZEVEC(char, current_inputline, buffer_len);
}
memcpy(current_inputline, line, len);
current_inputline[len] = ';';
current_inputline[len + 1] = '\0';
// run the parser on the instruction
// it will return a list of chained "insn",
// the first contains the opcode of the instruction
// and may be followed by other "insn" like an address
state = parse(current_inputline);
if (state == NO_INSN_GENERATED || !insn)
return;
// add 4 bytes to the fragment code buffer to put the new instruction
// and get buffer pointer (toP) on where to write the instruction
insn_size = 4;
toP = frag_more(insn_size);
#ifdef DEBUG
printf("INS: %s\n", line);
#endif
md_number_to_chars(toP, insn->value, insn_size); // put the 4-byte instruction into the current fragment code buffer
// loop over the output of the parser to see if there is a relocation to be performed
while (insn)
{
if (insn->reloc && insn->exp->symbol)
{
//DEBUG_TRACE("insn->reloc && insn->exp->symbol BFD_ARELOC_ESP32ULP_PUSH size =%i, insn->exp->value=%i, insn->pcrel=%i, insn->reloc=%i\n", size, (unsigned int)insn->exp->value, insn->pcrel, insn->reloc);
// generate a relocation request for this instruction so that linker will put the right address
// toP is the pointer on this instruction in the buffer of the current code fragment
// frag_now->fr_literal is the pointer on the begining of the buffer of the current code fragment
fix_new(frag_now, toP - frag_now->fr_literal, insn_size, insn->exp->symbol, insn->exp->value, insn->pcrel, insn->reloc);
}
#ifdef DEBUG
//DEBUG_TRACE(" reloc : value = %08x, pc=%08x, reloc=%08x BFD_RELOC_ESP32ULP_PLTPC = %08x\n", (unsigned int)insn->value, (unsigned int)insn->pcrel, (unsigned int)insn->reloc, BFD_RELOC_ESP32ULP_PLTPC);
if (insn->exp != ((void*)(0)))
{
//DEBUG_TRACE(" exp: %08x, sy_obj=%i\n", insn->exp->value, insn->exp->symbol->sy_obj.local);
}
#endif
insn = insn->next;
}
#ifdef OBJ_ELF
dwarf2_emit_insn(insn_size);
//DEBUG_TRACE("dya_pass ============== >insn_size=%i\n", (unsigned int)insn_size);
#endif
while (*line++ != '\0')
if (*line == '\n')
bump_line_counters();
}
I'm passing toP - frag_now->fr_literal as the offset to fix_new(). This is done in a similar way in several other architecture implementation. It is of course important to NOT increment toP during the while(), ie. we need to correct the second bug.
We could also use directly frag_now_fix() to get the offset of the instruction in the code fragment, w/o forgetting to substract 4 bytes since it has been incremented by the call to frag_more() a few lines above...
Also, please correct the issue on line 2705 of binutils/stabs.c:
if (**pp == ';' || *pp == '\0')
to be changed to
if (**pp == ';' || **pp == '\0')
The compilers outputs a big warning (!). Issue has been correctly several versions ago in the original binutils.
You may also consider removing all the other processor architecture and keep only esp32ulp, that would make the exe smaller.
Testing out the ULP and now written a program in an .S file. But it doesn't get compiled. Have the latest download that you link in the wiki.
The errors:
/home/user/esp/esp-idf/components/ulp/component_ulp_common.mk:28: warning: undefined variable 'ULP_LD_TEMPLATE'
make[1]: *** No rule to make target 'hall_sensor.ulp.o', needed by 'ulp_main.elf'. Stop.
make: *** [C:/msys32/home/user/esp/esp-idf/make/project.mk:448: component-main-build] Error 2
Have followed the instructions on https://esp-idf.readthedocs.io/en/latest/api-guides/ulp.html
component.mk:
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
ULP_S_SOURCES =$(addprefix $ (COMPONENT_PATH)/ulp/, hall_sensor.S)
ULP_EXP_DEP_OBJECTS := main.o
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
Any help is appreciated.
The legacy gnu make instructions for esp32 binutils https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/ulp-legacy.html states that the repository should be downloaded and the 'bin' folder should be added to the PATH variable. But regardless of binutils release, the 'bin' folder does not exist, and no other clarification is available.
Hi
Of course this issue appears only on large assembly programs
A JUMP instruction with absolute adress beyond 01FF will generate this error:
Error: rel too far BFD_RELOC_16
I beleive i have found the issue in file ....\gas\config\tc-esp32s2ulp.c line 115:
case BFD_RELOC_ESP32S2ULP_16_IMM:
if (fixP->fx_addsy != NULL)// relocation will be done not in linker
{
asymbol *sym = symbol_get_bfdsym(fixP->fx_addsy);
int force_reloc = S_FORCE_RELOC(fixP->fx_addsy, 1);
//printf("force_reloc = %i \n", force_reloc);
if (force_reloc != 0) if (sym->section->flags != 0) value = value >> 2;
}
if ((value < 0) || (value > 2047)) // <<<<<<<<<<<<<<<<<<<<<<<< HERE !
as_bad_where(fixP->fx_file, fixP->fx_line, _("rel too far BFD_RELOC_16"));
if (value > 2047) it generates an error. the value should'nt it be 8191 instead ?
best regards,
Jean Marc
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.