summaryrefslogtreecommitdiffstats
path: root/zpu/sw/emulation/zpuemu.c
diff options
context:
space:
mode:
Diffstat (limited to 'zpu/sw/emulation/zpuemu.c')
-rw-r--r--zpu/sw/emulation/zpuemu.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/zpu/sw/emulation/zpuemu.c b/zpu/sw/emulation/zpuemu.c
new file mode 100644
index 0000000..bf2f50b
--- /dev/null
+++ b/zpu/sw/emulation/zpuemu.c
@@ -0,0 +1,493 @@
+/* ZPU emulation library
+ *
+ * (c) 2011, Martin Strubel <hackfin@section5.ch>
+ *
+ * Limited functionality: Only one core in chain supported.
+ *
+ */
+
+// These headers must be implemented by the JTAG interface to your
+// HW debug adapter
+#include "jtag.h"
+#include "jtag_intern.h"
+
+#include "zpuemu.h"
+
+#include "zpu-opcodes.h"
+#include <stdio.h>
+
+
+#define IRSIZE 4
+
+static unsigned char
+s_reg8[2];
+
+static JtagRegister
+ir_r = {
+ .data = &s_reg8[1],
+ .nbits = IRSIZE,
+ .flags = JTAGREG_LSB
+};
+
+static JtagRegister
+opcode_r = {
+ .data = &s_reg8[0],
+ .nbits = 8,
+ .flags = JTAGREG_MSB
+};
+
+static unsigned char
+s_reg16[2];
+
+static JtagRegister
+ctrl_r = {
+ .data = &s_reg16[0],
+ .nbits = 16,
+ .flags = JTAGREG_MSB
+};
+
+static unsigned char
+s_reg32[4];
+
+static JtagRegister
+data_r = {
+ .data = &s_reg32[0],
+ .nbits = 32,
+ .flags = JTAGREG_MSB
+};
+
+void select_dr(CpuContext *c, uint8_t dr)
+{
+ jtag_goto_state(c->jtag, s_jtag_shift_ir);
+ reg_set(&ir_r, 0, IRSIZE, dr);
+ shift_generic(c->jtag, &ir_r, NULL, ir_r.nbits, UPDATE);
+}
+
+void shiftout32(CpuContext *c, REGISTER *r, int mode)
+{
+ jtag_flush(c->jtag);
+ jtag_goto_state(c->jtag, s_jtag_shift_dr);
+ shift_generic(c->jtag, &data_r, &data_r, data_r.nbits, mode);
+ *r = reg_get(&data_r, 0, 32);
+}
+
+void shiftin16(CpuContext *c, REGISTER r, int mode)
+{
+ jtag_goto_state(c->jtag, s_jtag_shift_dr);
+ reg_set(&ctrl_r, 0, 16, r);
+ shift_generic(c->jtag, &ctrl_r, NULL, ctrl_r.nbits, mode);
+}
+
+void shiftout16(CpuContext *c, REGISTER *r, int mode)
+{
+ jtag_goto_state(c->jtag, s_jtag_shift_dr);
+ shift_generic(c->jtag, &ctrl_r, &ctrl_r, ctrl_r.nbits, mode);
+ *r = reg_get(&ctrl_r, 0, 16);
+}
+
+// Auxiliaries
+
+static
+void push_opcode(CpuContext *c, uint8_t opcode, int mode)
+{
+ opcode_r.data[0] = opcode;
+ jtag_goto_state(c->jtag, s_jtag_shift_dr);
+ shift_generic(c->jtag, &opcode_r, NULL, opcode_r.nbits, mode);
+}
+
+#if 0
+static
+void push_val16(CpuContext *c, uint16_t val)
+{
+ int i = 14;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC); i -= 7;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC); i -= 7;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC);
+ push_opcode(c, OPCODE_NOP, EXEC);
+}
+#endif
+
+static
+void push_val32(CpuContext *c, uint32_t val)
+{
+ int i = 28;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC); i -= 7;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC); i -= 7;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC); i -= 7;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC); i -= 7;
+ push_opcode(c, OPCODE_IM | ((val >> i) & 0x7f), EXEC);
+ push_opcode(c, OPCODE_NOP, EXEC);
+}
+
+static
+uint32_t mem_read32(CpuContext *c, uint32_t addr)
+{
+ REGISTER r;
+ int q = jtag_queue(c->jtag, 1);
+ select_dr(c, TAP_EMUIR);
+ push_opcode(c, OPCODE_PUSHSP, EXEC);
+ push_val32(c, addr);
+ push_opcode(c, OPCODE_LOAD, EXEC);
+ push_opcode(c, OPCODE_NOP, EXEC);
+ select_dr(c, TAP_EMUDATA);
+ shiftout32(c, &r, UPDATE); // Execute Stack fixup
+ select_dr(c, TAP_EMUIR);
+ push_opcode(c, OPCODE_LOADSP | (LOADSP_INV ^ 0x01), EXEC);
+ push_opcode(c, OPCODE_POPSP, EXEC);
+ push_opcode(c, OPCODE_NOP, EXEC);
+ jtag_queue(c->jtag, q);
+ return r;
+}
+
+// Little read cache:
+struct cache {
+ ADDR addr;
+ uint32_t val;
+} g_cache = { 0xffffffff, 0 };
+
+static
+uint32_t mem_read32_cached(CpuContext *c, ADDR a)
+{
+ if (a != g_cache.addr) {
+ g_cache.val = mem_read32(c, a);
+ g_cache.addr = a;
+ // printf("Read %08x\n", g_cache.val);
+ }
+ return g_cache.val;
+}
+
+uint8_t mem_read8(CpuContext *c, ADDR a)
+{
+ uint32_t v;
+ int shift = (3 - (a & 0x3)) << 3;
+ a &= ~0x3;
+ // printf("shift: %d\n", shift);
+ v = mem_read32_cached(c, a) >> shift;
+ return v;
+}
+
+uint16_t mem_read16(CpuContext *c, ADDR a)
+{
+ uint32_t v;
+ int shift = (2 - (a & 0x2)) << 3;
+ a &= ~0x3;
+ // printf("shift: %d\n", shift);
+ v = mem_read32_cached(c, a) >> shift;
+ return v;
+}
+
+void mem_write32(CpuContext*c, uint32_t addr, uint32_t val)
+{
+ // Invalidate cache, when we're writing to the same addr:
+ if (g_cache.addr == (addr)) {
+ g_cache.addr = 0xffffffff;
+ }
+ select_dr(c, TAP_EMUIR);
+ push_opcode(c, OPCODE_PUSHSP, EXEC);
+ push_val32(c, val);
+ push_val32(c, addr); // Address
+ push_opcode(c, OPCODE_STORE, EXEC);
+ push_opcode(c, OPCODE_LOADSP | (LOADSP_INV ^ 0x00), EXEC);
+ push_opcode(c, OPCODE_POPSP, EXEC);
+ push_opcode(c, OPCODE_NOP, UPDATE);
+}
+
+
+void mem_write16(CpuContext*c, uint32_t addr, uint16_t val)
+{
+ int shift = (2 - (addr & 0x2)) << 3;
+ uint32_t v;
+
+ addr &= ~0x3;
+ v = mem_read32_cached(c, addr);
+
+ v = (v & ~(0xffff << shift)) | (val << shift);
+ mem_write32(c, addr, v);
+}
+
+void mem_write8(CpuContext *c, uint32_t addr, uint8_t val)
+{
+ int shift = (3 - (addr & 0x3)) << 3;
+ uint32_t v;
+
+ addr &= ~0x3;
+ v = mem_read32_cached(c, addr);
+
+ v = (v & ~(0xff << shift)) | (val << shift);
+ mem_write32(c, addr, v);
+}
+
+int enter_emulation(CpuContext *c)
+{
+ REGISTER r;
+
+ g_cache.addr = 0xffffffff; // Invalidate cache
+
+ // puts(">>> Enter emulation");
+ select_dr(c, TAP_EMUCTRL);
+ r = EMUREQ;
+ shiftin16(c, r, UPDATE);
+
+ return 0;
+}
+
+int leave_emulation(CpuContext *c)
+{
+ int error = 0;
+
+ // Turn off emulation bit
+ select_dr(c, TAP_EMUCTRL);
+ shiftin16(c, 0, UPDATE);
+
+ // Run some emulated opcodes:
+ // Return from emulation:
+ select_dr(c, TAP_EMUIR);
+ push_opcode(c, OPCODE_EMULEAVE, EXEC);
+ return error;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// API calls
+
+int zpu_emuinit(CpuContext *c, CONTROLLER jtag)
+{
+ c->jtag = jtag;
+ return 0;
+}
+
+int zpu_getid(CpuContext *c, uint32_t *code)
+{
+ select_dr(c, TAP_IDCODE);
+ shiftout32(c, code, UPDATE);
+ return 0;
+}
+
+int zpu_resume(CpuContext *c, int step)
+{
+ jtag_flush(c->jtag);
+ if (step) {
+ select_dr(c, TAP_EMUCTRL);
+ shiftin16(c, EMUREQ, UPDATE);
+ select_dr(c, TAP_EMUIR);
+ push_opcode(c, OPCODE_EMULEAVE, EXEC);
+ } else {
+ leave_emulation(c);
+ }
+ return 0;
+}
+
+int zpu_emulation(CpuContext *c, int which)
+{
+ // FIXME: for multicore, this needs to change.
+ jtag_flush(c->jtag);
+ if (which) {
+ enter_emulation(c);
+ } else {
+ leave_emulation(c);
+ }
+ return 0;
+}
+
+int zpu_state(CpuContext *c, uint16_t *state)
+{
+ REGISTER r;
+ int error = 0;
+
+ select_dr(c, TAP_EMUSTAT);
+ shiftout16(c, &r, UPDATE);
+ *state = r;
+ return error;
+}
+
+int zpu_reset(CpuContext *c, int mode)
+{
+ // TODO: Implement system control register on zealot
+ return 0;
+}
+
+int zpu_setreg(CpuContext *c, int regno, REGISTER val)
+{
+ switch (regno) {
+ case REG_PC:
+ select_dr(c, TAP_EMUIR);
+ push_val32(c, val);
+ push_opcode(c, OPCODE_POPPC, EXEC);
+ break;
+ case REG_SP:
+ select_dr(c, TAP_EMUIR);
+ push_val32(c, val);
+ push_opcode(c, OPCODE_POPSP, EXEC);
+ break;
+ default: return -1;
+ }
+ return 0;
+}
+
+int zpu_getreg(CpuContext *c, int regno, REGISTER *val)
+{
+ REGISTER r;
+ int q = jtag_queue(c->jtag, 1);
+ switch (regno) {
+ case REG_PC:
+ // XXX needed to update dbg_o.<signal>:
+ select_dr(c, TAP_EMUIR); // XXX
+ push_opcode(c, OPCODE_NOP, EXEC); // XXX
+ select_dr(c, TAP_DBGPC);
+ shiftout32(c, &r, UPDATE);
+ break;
+ case REG_SP:
+ select_dr(c, TAP_EMUIR);
+ push_opcode(c, OPCODE_PUSHSP, EXEC);
+ // XXX needed to update dbg_o.<signal>:
+ push_opcode(c, OPCODE_NOP, EXEC); // XXX
+ push_opcode(c, OPCODE_POPSP, UPDATE); // queue, exec later
+ select_dr(c, TAP_EMUDATA);
+ shiftout32(c, &r, EXEC); // (here)
+ break;
+ default: return -1;
+ }
+ *val = r;
+ jtag_queue(c->jtag, q);
+ return 0;
+}
+
+void zpu_dumpstat(CpuContext *c)
+{
+ REGISTER r;
+
+ select_dr(c, TAP_EMUSTAT);
+ shiftout16(c, &r, UPDATE);
+ printf("EMUSTAT: %04x -", r & 0xffff);
+ if (r) {
+ if (r & ZPU_IDIM) printf(" [IDIM]");
+ if (r & ZPU_INRESET) printf(" [RESET]");
+ if (r & ZPU_BREAK) printf(" [BREAK]");
+ if (r & EMUACK) printf(" [EMUACK]");
+ if (r & EMURDY) printf(" [EMURDY]");
+ if (r & ZPU_MEMBUSY) printf(" [MEM_BUSY]");
+ }
+ printf("\n");
+ select_dr(c, TAP_COUNT1);
+ shiftout32(c, &r, UPDATE);
+ printf("COUNT1: %012d\n", r);
+ select_dr(c, TAP_COUNT2);
+ shiftout16(c, &r, UPDATE);
+ printf("COUNT2: %08d\n", r);
+}
+
+int guess_access(ADDR addr, unsigned int *count)
+{
+ int sizecode;
+ // I/O space wants to be addressed long word wise:
+ if (addr >= 0x80080000 && *count == 4) {
+ *count = 1;
+ return LDST_32;
+ }
+ // if we have even addresses and even count, we can
+ // use word size transfers instead of byte wise.
+ switch (addr % 4) {
+ case 0:
+ switch (*count % 4) {
+ case 0:
+ sizecode = LDST_32;
+ *count /= 4;
+ break;
+ case 2:
+ sizecode = LDST_16;
+ *count /= 2;
+ break;
+ default:
+ sizecode = LDST_8;
+ break;
+ }
+ break;
+ case 2:
+ if (*count % 2 == 0) {
+ sizecode = LDST_16;
+ *count /= 2;
+ } else {
+ sizecode = LDST_8;
+ }
+ break;
+ default:
+ sizecode = LDST_8;
+ }
+ return sizecode;
+}
+
+
+int zpu_mem_read(CpuContext *c, ADDR addr, unsigned int count,
+ unsigned char *buf)
+{
+ int sz;
+ uint32_t v;
+ int q = jtag_queue(c->jtag, 1);
+
+ sz = guess_access(addr, &count);
+
+ switch (sz) {
+ case LDST_8:
+ while (count--) {
+ *buf++ = mem_read8(c, addr++);
+ }
+ break;
+ case LDST_16:
+ while (count--) {
+ v = mem_read16(c, addr); addr += 2;
+ buf[1] = v; v >>= 8;
+ buf[0] = v;
+ buf += 2;
+ }
+ break;
+ case LDST_32:
+ while (count--) {
+ v = mem_read32(c, addr); addr += 4;
+ buf[3] = v; v >>= 8;
+ buf[2] = v; v >>= 8;
+ buf[1] = v; v >>= 8;
+ buf[0] = v;
+ buf += 4;
+ }
+ break;
+ }
+ jtag_flush(c->jtag);
+ jtag_queue(c->jtag, q);
+ return 0;
+}
+
+int zpu_mem_write(CpuContext *c, ADDR addr, unsigned int count,
+ const unsigned char *buf)
+{
+ int sz;
+ uint32_t v;
+ int q = jtag_queue(c->jtag, 1);
+
+ sz = guess_access(addr, &count);
+
+ switch (sz) {
+ case LDST_8:
+ // XXX: Could be optimized further
+ while (count--) {
+ v = *buf++;
+ mem_write8(c, addr, v); addr++;
+ }
+ break;
+ case LDST_16:
+ while (count--) {
+ v = (buf[0] << 8) | buf[1];
+ mem_write16(c, addr, v);
+ addr += 2; buf += 2;
+ }
+ break;
+ case LDST_32:
+ while (count--) {
+ v = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ mem_write32(c, addr, v);
+ addr += 4; buf += 4;
+ }
+ break;
+ }
+ jtag_flush(c->jtag);
+ jtag_queue(c->jtag, q);
+ return 0;
+}
OpenPOWER on IntegriCloud