binutils-esp32ulp icon indicating copy to clipboard operation
binutils-esp32ulp copied to clipboard

Here is ULP disassembler (esp32ulp-elf-objdump)

Open 8785benjamin opened this issue 6 years ago • 0 comments

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

8785benjamin avatar May 31 '18 14:05 8785benjamin