Skip to content

Here is ULP disassembler (esp32ulp-elf-objdump) #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
8785benjamin opened this issue May 31, 2018 · 0 comments
Open

Here is ULP disassembler (esp32ulp-elf-objdump) #10

8785benjamin opened this issue May 31, 2018 · 0 comments

Comments

@8785benjamin
Copy link
Contributor

8785benjamin commented May 31, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant