Giter Site home page Giter Site logo

espressif / binutils-esp32ulp Goto Github PK

View Code? Open in Web Editor NEW
46.0 12.0 18.0 283.51 MB

Binutils fork with support for the ESP32 ULP co-processor

License: GNU General Public License v2.0

Makefile 13.02% M4 0.58% C 53.48% Objective-C 0.01% DIGITAL Command Language 0.03% Shell 0.56% C++ 3.10% Perl 0.11% Lex 0.18% Yacc 0.43% Emacs Lisp 0.01% Assembly 15.79% DTrace 4.18% Rust 0.01% D 6.62% Mathematica 0.01% Roff 0.56% R 0.26% Scheme 1.06% Batchfile 0.01%

binutils-esp32ulp's Introduction

		   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.

binutils-esp32ulp's People

Contributors

amodra avatar andreas-schwab avatar brobecke avatar djdelorierh avatar edelsohn avatar eli-zaretskii avatar fche avatar gingold-adacore avatar github-cygwin avatar hjl-tools avatar hpataxisdotcom avatar ianlancetaylor avatar jakubjelinek avatar jankratochvil avatar jasonmolenda avatar jim-wilson avatar jjohnstn avatar johntconklin avatar jsm28 avatar kevinbuettner avatar muller-sourceware-org avatar nickclifton avatar palves avatar perbothner avatar rsandifo avatar shtiek avatar thorpej avatar vapier avatar vprus avatar xdje42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

binutils-esp32ulp's Issues

JUMPS inside macro doesn't work

Code

#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

What do I expect:

The output of RTC GPIO 10 aka GPIO 4 stays high for about a second and then low for about a second.

What do I get:

error
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).

compile error

/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

esp32s2ulp-parse.y:927:18: error: 'BFD_RELOC_ESP32S2ULP_GOT17M4' undeclared

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:

  • UNAME_v=FreeBSD 14.0-CURRENT 1400012
  • gcc 10.3.0
  • git tag: v2.28.51-esp-20191205

Compilation Error

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

Incorectly translated 'JUMP Rx` instruction.

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 thatjump r0` and so on.

How to install older version?

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.

How to use under Windows?

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

Here is ULP disassembler (esp32ulp-elf-objdump)

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/dis-asm.h:
      extern int print_insn_esp32ulp (bfd_vma, disassemble_info *);
      */

#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);

}

esp32ulp-dis.txt

arm64 distro?

Hi, there's a build for arm64?
I have issue on my rpi installing esp-idf ->I know is not supported :-)

Compilation Error

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

Assembly of MOVE Rx, Ry

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?

Incorrect relative address error

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.

Relocation 2 not handled in gas. Contact Support.

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.

Different JUMP with global / not-global symbol

Overview

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

Current behaviour

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.

Expected behaviour

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.

ARM version broken for ULP example

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```

Negative division for Byte->Word calculations

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:

  1. Leave things as they are. It works (at least as I would expect it - except for to possibility of -0).
  2. Change all places where bytes are converted to words from >>2 to /4 operations. This should express the intent better, and the compiler can optimise these as necessary/possible.
  3. Add a check to functions, which generate jump instructions (such as esp32ulp_cmd_jump_relr_esp32), to only accept step_val inputs that are multiples of 4.
  4. "Fix" my recent PR (#18), by wrapping 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?
  5. Do option 2 and option 5 above. This would get the divide-by-4 behaviour, and might avoid surprises to others down the line, with what the macro does.

@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.

Relocation information not generated correctly

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.

Can't build .S files

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.

JUMP cannot reach address beyond 1FF (2047)

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.