diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/lpcboot/lpc43xx-spifi.c | 107 | ||||
-rw-r--r-- | app/lpcboot/lpcboot.c | 325 | ||||
-rw-r--r-- | app/lpcboot/miniloader.S | 58 | ||||
-rw-r--r-- | app/lpcboot/rules.mk | 10 | ||||
-rw-r--r-- | app/mdebug/fw-m0sub.S | 519 | ||||
-rw-r--r-- | app/mdebug/fw-m0sub.h | 186 | ||||
-rw-r--r-- | app/mdebug/mdebug.c | 160 | ||||
-rw-r--r-- | app/mdebug/rswd.c | 247 | ||||
-rw-r--r-- | app/mdebug/rswdp.h | 116 | ||||
-rw-r--r-- | app/mdebug/rules.mk | 14 | ||||
-rw-r--r-- | app/mdebug/swd-m0sub.c | 197 | ||||
-rw-r--r-- | app/mdebug/swd-sgpio.c | 366 | ||||
-rw-r--r-- | app/mdebug/swd.h | 55 | ||||
-rw-r--r-- | app/mdebug/swo-uart1.c | 151 | ||||
-rw-r--r-- | app/udctest/rules.mk | 9 | ||||
-rw-r--r-- | app/udctest/udctest.c | 110 |
16 files changed, 2630 insertions, 0 deletions
diff --git a/app/lpcboot/lpc43xx-spifi.c b/app/lpcboot/lpc43xx-spifi.c new file mode 100644 index 00000000..73968edd --- /dev/null +++ b/app/lpcboot/lpc43xx-spifi.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> +#include <reg.h> + +#include <platform/lpc43xx-spifi.h> + +#define CMD_PAGE_PROGRAM 0x02 +#define CMD_READ_DATA 0x03 +#define CMD_READ_STATUS 0x05 +#define CMD_WRITE_ENABLE 0x06 +#define CMD_SECTOR_ERASE 0x20 + +static void spifi_write_enable(void) { + writel(CMD_FF_SERIAL | CMD_FR_OP | CMD_OPCODE(CMD_WRITE_ENABLE), + SPIFI_CMD); + while (readl(SPIFI_STAT) & STAT_CMD) ; +} + +static void spifi_wait_busy(void) { + while (readl(SPIFI_STAT) & STAT_CMD) ; + writel(CMD_POLLBIT(0) | CMD_POLLCLR | CMD_POLL | + CMD_FF_SERIAL | CMD_FR_OP | CMD_OPCODE(CMD_READ_STATUS), + SPIFI_CMD); + while (readl(SPIFI_STAT) & STAT_CMD) ; + // discard matching status byte from fifo + readb(SPIFI_DATA); +} + +void spifi_page_program(u32 addr, u32 *ptr, u32 count) { + spifi_write_enable(); + writel(addr, SPIFI_ADDR); + writel(CMD_DATALEN(count * 4) | CMD_FF_SERIAL | CMD_FR_OP_3B | + CMD_DOUT | CMD_OPCODE(CMD_PAGE_PROGRAM), SPIFI_CMD); + while (count-- > 0) { + writel(*ptr++, SPIFI_DATA); + } + spifi_wait_busy(); +} + +void spifi_sector_erase(u32 addr) { + spifi_write_enable(); + writel(addr, SPIFI_ADDR); + writel(CMD_FF_SERIAL | CMD_FR_OP_3B | CMD_OPCODE(CMD_SECTOR_ERASE), + SPIFI_CMD); + spifi_wait_busy(); +} + +int spifi_verify_erased(u32 addr, u32 count) { + int err = 0; + writel(addr, SPIFI_ADDR); + writel(CMD_DATALEN(count * 4) | CMD_FF_SERIAL | CMD_FR_OP_3B | + CMD_OPCODE(CMD_READ_DATA), SPIFI_CMD); + while (count-- > 0) { + if (readl(SPIFI_DATA) != 0xFFFFFFFF) err = -1; + } + while (readl(SPIFI_STAT) & STAT_CMD) ; + return err; +} + +int spifi_verify_page(u32 addr, u32 *ptr) { + int count = 256 / 4; + int err = 0; + writel(addr, SPIFI_ADDR); + writel(CMD_DATALEN(count * 4) | CMD_FF_SERIAL | CMD_FR_OP_3B | + CMD_OPCODE(CMD_READ_DATA), SPIFI_CMD); + while (count-- > 0) { + if (readl(SPIFI_DATA) != *ptr++) err = -1; + } + while (readl(SPIFI_STAT) & STAT_CMD) ; + return err; +} + +// at reset-stop, all clocks are running from 12MHz internal osc +// todo: run SPIFI_CLK at a much higher rate +// todo: use 4bit modes +void spifi_init(void) { + // reset spifi controller + writel(STAT_RESET, SPIFI_STAT); + while (readl(SPIFI_STAT) & STAT_RESET) ; + writel(0xFFFFF, SPIFI_CTRL); +} + diff --git a/app/lpcboot/lpcboot.c b/app/lpcboot/lpcboot.c new file mode 100644 index 00000000..fcd05937 --- /dev/null +++ b/app/lpcboot/lpcboot.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2015 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <app.h> +#include <err.h> +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> +#include <dev/udc.h> + +#include <platform.h> +#include <arch/arm.h> +#include <kernel/thread.h> +#include <kernel/event.h> +#include <kernel/timer.h> + +#include <platform/lpc43xx-gpio.h> + +#define PIN_LED PIN(1,1) +#define GPIO_LED GPIO(0,8) + +void spifi_init(void); +void spifi_page_program(u32 addr, u32 *ptr, u32 count); +void spifi_sector_erase(u32 addr); +int spifi_verify_erased(u32 addr, u32 count); +int spifi_verify_page(u32 addr, u32 *ptr); + +static event_t txevt = EVENT_INITIAL_VALUE(txevt, 0, 0); +static event_t rxevt = EVENT_INITIAL_VALUE(rxevt, 0, 0); + +static udc_request_t *txreq; +static udc_request_t *rxreq; +static udc_endpoint_t *txept; +static udc_endpoint_t *rxept; + +static volatile int online; +static volatile int txstatus; +static volatile int rxstatus; +static volatile unsigned rxactual; + +static void lpcboot_notify(udc_gadget_t *gadget, unsigned event) { + if (event == UDC_EVENT_ONLINE) { + online = 1; + } else { + online = 0; + } +} + +static void rx_complete(udc_request_t *req, unsigned actual, int status) { + rxactual = actual; + rxstatus = status; + event_signal(&rxevt, 0); +} + +static void tx_complete(udc_request_t *req, unsigned actual, int status) { + txstatus = status; + event_signal(&txevt, 0); +} + +void usb_xmit(void *data, unsigned len) { + event_unsignal(&txevt); + txreq->buffer = data; + txreq->length = len; + txstatus = 1; + udc_request_queue(txept, txreq); + event_wait(&txevt); +} + +int usb_recv(void *data, unsigned len, lk_time_t timeout) { + event_unsignal(&rxevt); + rxreq->buffer = data; + rxreq->length = len; + rxstatus = 1; + udc_request_queue(rxept, rxreq); + if (event_wait_timeout(&rxevt, timeout)) { + return ERR_TIMED_OUT; + } + return rxactual; +} + +static udc_device_t lpcboot_device = { + .vendor_id = 0x1209, + .product_id = 0x5039, + .version_id = 0x0100, +}; + +static udc_endpoint_t *lpcboot_endpoints[2]; + +static udc_gadget_t lpcboot_gadget = { + .notify = lpcboot_notify, + .ifc_class = 0xFF, + .ifc_subclass = 0xFF, + .ifc_protocol = 0xFF, + .ifc_endpoints = 2, + .ept = lpcboot_endpoints, +}; + +static void lpcboot_init(const struct app_descriptor *app) +{ + udc_init(&lpcboot_device); + lpcboot_endpoints[0] = txept = udc_endpoint_alloc(UDC_BULK_IN, 512); + lpcboot_endpoints[1] = rxept = udc_endpoint_alloc(UDC_BULK_OUT, 512); + txreq = udc_request_alloc(); + rxreq = udc_request_alloc(); + rxreq->complete = rx_complete; + txreq->complete = tx_complete; + udc_register_gadget(&lpcboot_gadget); +} + +#define RAM_BASE 0x10000000 +#define RAM_SIZE (128 * 1024) + +#define BOOT_BASE 0 +#define BOOT_SIZE (32 * 1024) + +#define ROM_BASE (32 * 1024) +#define ROM_SIZE (128 * 1024) + +struct device_info { + u8 part[16]; + u8 board[16]; + u32 version; + u32 ram_base; + u32 ram_size; + u32 rom_base; + u32 rom_size; + u32 unused0; + u32 unused1; + u32 unused2; +}; + +struct device_info DEVICE = { + .part = "LPC43xx", + .board = TARGET, + .version = 0x0001000, + .ram_base = RAM_BASE, + .ram_size = RAM_SIZE, + .rom_base = ROM_BASE, + .rom_size = ROM_SIZE, +}; + + +#define MAGIC1 0xAA113377 +#define MAGIC2 0xAA773311 +#define MAGIC1_ADDR 0x20003FF8 +#define MAGIC2_ADDR 0x20003FFC + +void boot_app(void) { + writel(MAGIC1, MAGIC1_ADDR); + writel(MAGIC2, MAGIC2_ADDR); +} + +int erase_page(u32 addr) { + spifi_sector_erase(addr); + return spifi_verify_erased(addr, 0x1000/4); +} + +int write_page(u32 addr, void *ptr) { + unsigned n; + u32 *x = ptr; + for (n = 0; n < 16; n++) { + spifi_page_program(addr, x, 256 / 4); + if (spifi_verify_page(addr, x)) return -1; + addr += 256; + x += (256 / 4); + } + return 0; +} + +static uint32_t ram[4096/4]; + +void handle(u32 magic, u32 cmd, u32 arg) { + u32 reply[2]; + u32 addr, xfer; + int err = 0; + + if (magic != 0xDB00A5A5) + return; + + reply[0] = magic; + reply[1] = -1; + + switch (cmd) { + case 'E': + reply[1] = erase_page(ROM_BASE); + break; + case 'W': + case 'w': + if (cmd == 'W') { + if (arg > ROM_SIZE) + break; + addr = ROM_BASE; + } else { + if (arg > BOOT_SIZE) + break; + addr = BOOT_BASE; + } + reply[1] = 0; + usb_xmit(reply, 8); + while (arg > 0) { + xfer = (arg > 4096) ? 4096 : arg; + usb_recv(ram, xfer, INFINITE_TIME); + if (!err) err = erase_page(addr); + if (!err) err = write_page(addr, ram); + addr += 4096; + arg -= xfer; + } + printf("flash %s\n", err ? "ERROR" : "OK"); + reply[1] = err; + break; +#if WITH_BOOT_TO_RAM + case 'X': + if (arg > RAM_SIZE) + break; + reply[1] = 0; + usb_xmit(reply, 8); + usb_recv(ram, arg); + usb_xmit(reply, 8); + + /* let last txn clear */ + usb_recv_timeout(buf, 64, 10); + + boot_image(ram); + break; +#endif + case 'Q': + reply[1] = 0; + usb_xmit(reply, 8); + usb_xmit(&DEVICE, sizeof(DEVICE)); + return; + case 'A': + boot_app(); + case 'R': + /* reboot "normally" */ + reply[1] = 0; + usb_xmit(reply, 8); + udc_stop(); + platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET); + default: + break; + } + usb_xmit(reply, 8); +} + +static short led_idx = 0; +static short led_delay[] = { 500, 100, 100, 100, }; +static short led_state[] = { 1, 0, 1, 0, }; +static timer_t led_timer = TIMER_INITIAL_VALUE(led_timer); + +static enum handler_return led_timer_cb(timer_t *timer, lk_time_t now, void *arg) { + gpio_set(GPIO_LED, led_state[led_idx]); + timer_set_oneshot(timer, led_delay[led_idx], led_timer_cb, NULL); + led_idx++; + if (led_idx == (sizeof(led_state)/sizeof(led_state[0]))) { + led_idx = 0; + } + return 0; +} + +static void lpcboot_entry(const struct app_descriptor *app, void *args) +{ + lk_time_t timeout; + int r; + u32 buf[64/4]; + +#if 0 + timeout = INFINITE_TIME; +#else + if (readl(32768) != 0) { + timeout = 3000; + } else { + timeout = INFINITE_TIME; + } +#endif + + pin_config(PIN_LED, PIN_MODE(0) | PIN_PLAIN); + gpio_config(GPIO_LED, GPIO_OUTPUT); + led_timer_cb(&led_timer, 0, NULL); + + udc_start(); + spifi_init(); + for (;;) { + if (!online) { + thread_yield(); + continue; + } + r = usb_recv(buf, 64, timeout); + if (r == ERR_TIMED_OUT) { + boot_app(); + platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET); + } + if (r == 12) { + handle(buf[0], buf[1], buf[2]); + timeout = INFINITE_TIME; + } + } +} + +APP_START(usbtest) + .init = lpcboot_init, + .entry = lpcboot_entry, +APP_END + + diff --git a/app/lpcboot/miniloader.S b/app/lpcboot/miniloader.S new file mode 100644 index 00000000..1bba9fc8 --- /dev/null +++ b/app/lpcboot/miniloader.S @@ -0,0 +1,58 @@ + +.syntax unified + +miniloader_vectors: + .word 0x20003FF0 + .word miniloader_reset + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + .word miniloader_fault + 1 + +// miniloader_boot(unsigned src, unsigned count) +miniloader_boot: + mov r2, #0x10000000 +miniloader_boot_loop: + ldr r3, [r0], #4 + str r3, [r2], #4 + subs r1, #1 + bne miniloader_boot_loop + mov r0, #0x10000000 + ldr sp, [r0] + ldr r0, [r0, #4] + bx r0 + +miniloader_reset: + ldr r0, =0x20003FF8 + ldr r1, =0xAA113377 + ldr r2, =0xAA773311 + ldr r3, [r0] + ldr r4, [r0, #4] + mov r5, #0 + str r5, [r0] + str r5, [r0, #4] + cmp r1, r3 + bne start_bootloader + cmp r2, r4 + bne start_bootloader +start_app: + ldr r0, =0x8000 + ldr r1, =(131072/4) + b miniloader_boot +start_bootloader: + ldr r0, =0x00001000 + ldr r1, =(32768/4) + b miniloader_boot + +miniloader_fault: + b . diff --git a/app/lpcboot/rules.mk b/app/lpcboot/rules.mk new file mode 100644 index 00000000..0e1c692c --- /dev/null +++ b/app/lpcboot/rules.mk @@ -0,0 +1,10 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/lpcboot.c \ + $(LOCAL_DIR)/lpc43xx-spifi.c + +include make/module.mk + diff --git a/app/mdebug/fw-m0sub.S b/app/mdebug/fw-m0sub.S new file mode 100644 index 00000000..b7185b51 --- /dev/null +++ b/app/mdebug/fw-m0sub.S @@ -0,0 +1,519 @@ +/* fw-m0sub.S + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.syntax unified + +m0_vectors: + .word 0x18003FF0 + .word m0_reset + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 + .word m0_fault + 1 +// external IRQs + .word m0_fault + 1 + .word m0_irq + 1 + +m0_fault: + ldr r0, =0x18000000 + ldr r1, =0xeeee0000 + mrs r2, xpsr + movs r3, #0xFF + ands r2, r2, r3 + orrs r1, r1, r2 + str r1, [r0] + b . + +.ltorg + +#define REPORT_DELAY 0 + +#define COMM_BASE 0x18004000 + +#define COMM_CMD 0 +#define COMM_ARG0 4 +#define COMM_ARG1 8 +#define COMM_RESP 12 +#define COMM_RETRY 16 + + +#define M4_TXEV 0x40043130 // write 0 to clear + +#define SGPIO_BASE (0x40101210) +#define OFF_IN 0 +#define OFF_OUT 4 +#define OFF_OEN 8 +#define SGPIO_IN (0x40101210) +#define SGPIO_OUT (0x40101214) +#define SGPIO_OEN (0x40101218) + +#define CLK_BIT 11 +#define DIO_BIT 14 +#define TEN_BIT 15 +#define CLK_MSK (1 << CLK_BIT) +#define DIO_MSK (1 << DIO_BIT) +#define TEN_MSK (1 << TEN_BIT) + +#define CLK1_OUT (CLK_MSK | TEN_MSK) +#define CLK0_OUT (TEN_MSK) +#define CLK1_IN (CLK_MSK) +#define CLK0_IN (0) + +#define OEN_IN ((1 << CLK_BIT) | (1 << TEN_BIT)) +#define OEN_OUT ((1 << CLK_BIT) | (1 << DIO_BIT) | (1 << TEN_BIT)) + +#define NOP4 nop ; nop ; nop ; nop +#define NOP8 NOP4 ; NOP4 +#define NOP16 NOP8 ; NOP8 + +//#define DELAY nop ; nop +//#define DELAY NOP8 + +// r11 CLK1_OUT const +// r10 CLK0_OUT const +// r9 delay subroutine +// r8 comm_base addr +// r7 SGPIO_BASE addr +// r6 DIO_MSK const +// r5 CLK1_IN const +// r4 CLK0_IN const +// r3 outbits data + +snooze_2m: + nop ; nop ; nop ; nop + nop ; nop ; nop ; nop + nop ; nop ; nop ; nop + nop ; nop ; nop ; nop +snooze_3m: + nop ; nop ; nop ; nop + nop ; nop ; nop ; nop +snooze_4m: + nop ; nop ; nop ; nop + nop ; nop ; nop ; nop +snooze_6m: + nop ; nop ; nop ; nop +snooze_8m: + bx lr + +// delay 0 nops 16MHz +// delay 2 nops 12MHz +// delay 4 nops 9.6MHz +#define DELAY blx r9 + +// 12 cycles + DELAY x 2 +.macro ONE_BIT_OUT + lsls r2, r3, #DIO_BIT // shift bit 1 to posn + ands r2, r2, r6 // isolate bit 1 + movs r1, r2 // save bit 1 + add r2, r2, r10 // combine with CLK1 + DELAY + str r2, [r7, #OFF_OUT] // commit negative egde + lsrs r3, r3, #1 // advance to next bit + add r1, r1, r11 // combine with CLK1 + nop + nop + DELAY + str r1, [r7, #OFF_OUT] // commit positive edge +.endm + +.macro ONE_BIT_IN + ands r0, r0, r6 // isolate input bit + lsls r0, r0, #(31-DIO_BIT) // move to posn 31 + lsrs r3, r3, #1 // make room + orrs r3, r3, r0 // add bit + DELAY + str r4, [r7, #OFF_OUT] // commit negative edge + ldr r0, [r7, #OFF_IN] // sample input + nop + nop + DELAY + str r5, [r7, #OFF_OUT] // commit positive edge +.endm + +// used for the final parity and turn bits on input so this +// actually only reads one bit +read_2: + push {lr} + nop + nop + nop + nop + DELAY + str r4, [r7, #OFF_OUT] + ldr r0, [r7, #OFF_IN] + nop + nop + DELAY + str r5, [r7, #OFF_OUT] + ands r0, r0, r6 // isolate bit + lsrs r0, r0, #DIO_BIT // shift to bit0 + nop + nop + DELAY + str r4, [r7, #OFF_OUT] + nop + nop + nop + nop + DELAY + str r5, [r7, #OFF_OUT] + pop {pc} + +// w0: <15> <parity:1> <cmd:16> +// w1: <data:32> + + +write_16: + push {lr} + b _write_16 +write_32: + push {lr} + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT +_write_16: + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + ONE_BIT_OUT + pop {pc} +write_1: + push {lr} + ONE_BIT_OUT + pop {pc} + +read_4: + push {lr} + b _read_4 +read_32: + push {lr} + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN +_read_4: + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ONE_BIT_IN + ands r0, r0, r6 // isolate input bit + lsls r0, r0, #(31-DIO_BIT) // move to posn 31 + lsrs r3, r3, #1 // make room + orrs r3, r3, r0 // add bit + pop {pc} + +init: + ldr r0, =CLK1_OUT + mov r11, r0 + ldr r0, =CLK0_OUT + mov r10, r0 + ldr r0, =(snooze_4m + 1) + mov r9, r0 + ldr r0, =COMM_BASE + mov r8, r0 + ldr r7, =SGPIO_BASE + ldr r6, =DIO_MSK + ldr r5, =CLK1_IN + ldr r4, =CLK0_IN + bx lr + +#define MAX_RETRY 8192 + +err_fail: + movs r0, #3 + mov r3, r8 + str r0, [r3, #COMM_RESP]; + pop {pc} + +err_timeout: + movs r0, #2 + mov r3, r8 + str r0, [r3, #COMM_RESP]; + pop {pc} + +cmd_read_txn: + push {lr} + + ldr r0, =MAX_RETRY + //movs r0, #MAX_RETRY + mov r12, r0 + +rd_retry: + ldr r3, [r3, #COMM_ARG0] + bl write_16 + + ldr r3, =OEN_IN + str r3, [r7, #OFF_OEN] + bl read_4 + + lsrs r3, r3, #29 + cmp r3, #1 // OK + beq rd_okay + + ldr r1, =OEN_OUT + str r1, [r7, #OFF_OEN] + + cmp r3, #2 // WAIT + bne err_fail + + mov r0, r12 + subs r0, r0, #1 + mov r12, r0 + beq err_timeout + mov r3, r8 + b rd_retry + +rd_okay: + bl read_32 + bl read_2 + ldr r1, =OEN_OUT + str r1, [r7, #OFF_OEN] + mov r1, r11 + orrs r1, r1, r6 + str r1, [r7, #OFF_OUT] + + mov r1, r8 // get COMM_BASE + str r3, [r1, #COMM_ARG0] + str r0, [r1, #COMM_ARG1] + movs r0, #0 + str r0, [r1, #COMM_RESP] +#if REPORT_DELAY + mov r0, r12 + str r0, [r1, #COMM_RETRY] +#endif + pop {pc} + + +cmd_write_txn: + push {lr} + + ldr r0, =MAX_RETRY + mov r12, r0 + +wr_retry: + ldr r3, [r3, #COMM_ARG0] + bl write_16 + push {r3} // stash parity bit + + ldr r3, =OEN_IN + str r3, [r7, #OFF_OEN] + bl read_4 + + lsrs r3, r3, #29 + cmp r3, #1 // OK + beq wr_okay + + pop {r0} // discard saved parity bit + + ldr r1, =OEN_OUT + str r1, [r7, #OFF_OEN] + + cmp r3, #2 // WAIT + bne err_fail + + mov r0, r12 + subs r0, r0, #1 + mov r12, r0 + beq err_timeout + + mov r3, r8 + b wr_retry + +wr_okay: + ldr r3, =OEN_OUT + str r3, [r7, #OFF_OEN] + bl write_1 + + mov r3, r8 + ldr r3, [r3, #COMM_ARG1] + bl write_32 + + pop {r3} // recover parity bit + bl write_1 + + mov r3, r8 // get COMM_BASE + movs r0, #0 + str r0, [r3, #COMM_RESP] +#if REPORT_DELAY + mov r0, r12 + str r0, [r3, #COMM_RETRY] +#endif + pop {pc} + +cmd_reset: + push {lr} + ldr r3, =0xffffffff + mov r12, r3 + bl write_32 + mov r3, r12 + bl write_32 + + ldr r3, =0b1110011110011110 + bl write_16 + + mov r3, r12 + bl write_32 + mov r3, r12 + bl write_32 + + mov r3, r8 + movs r0, #0 + str r0, [r3, #COMM_RESP] + pop {pc} + + +m0_irq: + push {lr} + + // clear event from m4 + ldr r0, =M4_TXEV + movs r1, #0 + str r1, [r0] + + mov r3, r8 // get COMM_BASE + ldr r0, [r3, #COMM_CMD] + cmp r0, #5 + bls good_cmd + movs r0, #0 +good_cmd: + lsls r0, r0, #2 + adr r1, cmd_table + ldr r2, [r1, r0] + blx r2 + + pop {pc} + +.align 2 +cmd_table: + .word cmd_invalid + 1 + .word cmd_nop + 1 + .word cmd_read_txn + 1 + .word cmd_write_txn + 1 + .word cmd_reset + 1 + .word cmd_setclock + 1 + +cmd_invalid: + movs r0, #9 + str r0, [r3, #COMM_RESP] + bx lr + +cmd_nop: + movs r0, #0 + str r0, [r3, #COMM_RESP] + bx lr + +cmd_setclock: + ldr r0, [r3, #COMM_ARG0] + cmp r0, #8 + bls good_clock + movs r0, #0 +good_clock: + lsls r0, r0, #2 + adr r1, snooze_table + ldr r1, [r1, r0] + mov r9, r1 + + movs r0, #0 + str r0, [r3, #COMM_RESP] + bx lr + +.align 2 +snooze_table: + .word snooze_2m + 1 + .word snooze_2m + 1 + .word snooze_2m + 1 + .word snooze_3m + 1 + .word snooze_4m + 1 + .word snooze_4m + 1 + .word snooze_6m + 1 + .word snooze_6m + 1 + .word snooze_8m + 1 + +m0_reset: + ldr r0, =0x18000000 + ldr r1, =0xaaaa0000 + str r1, [r0] + + bl init + + // enable IRQ1 (Event From M4) + ldr r0, =0xE000E100 + movs r1, #2 + str r1, [r0] + +m0_idle: + wfi + b m0_idle diff --git a/app/mdebug/fw-m0sub.h b/app/mdebug/fw-m0sub.h new file mode 100644 index 00000000..173b358a --- /dev/null +++ b/app/mdebug/fw-m0sub.h @@ -0,0 +1,186 @@ +unsigned char zero_bin[] = { + 0xf0, 0x3f, 0x00, 0x18, 0x41, 0x08, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, + 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, + 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, + 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, + 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, + 0x49, 0x00, 0x00, 0x18, 0x49, 0x00, 0x00, 0x18, 0xc5, 0x07, 0x00, 0x18, + 0x04, 0x48, 0x05, 0x49, 0xef, 0xf3, 0x03, 0x82, 0xff, 0x23, 0x1a, 0x40, + 0x11, 0x43, 0x01, 0x60, 0xfe, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0xee, 0xee, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0x70, 0x47, 0x00, 0xb5, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x80, 0x0b, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7c, 0x60, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x00, 0xbd, 0x00, 0xb5, 0xc0, 0xe0, + 0x00, 0xb5, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, 0x52, 0x44, 0xc8, 0x47, + 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x79, 0x60, 0x00, 0xbd, 0x00, 0xb5, 0x9a, 0x03, 0x32, 0x40, 0x11, 0x00, + 0x52, 0x44, 0xc8, 0x47, 0x7a, 0x60, 0x5b, 0x08, 0x59, 0x44, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x79, 0x60, 0x00, 0xbd, 0x00, 0xb5, 0x34, 0xe1, + 0x00, 0xb5, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, + 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, + 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, + 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, + 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, + 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, + 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, + 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, + 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, + 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, + 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, + 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, + 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, + 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, + 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, + 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, + 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, + 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, + 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, + 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, + 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, + 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, + 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, + 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, + 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, + 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, + 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, + 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, + 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, + 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, + 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, + 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, + 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, + 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, + 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, + 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, + 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, + 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, + 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, + 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, + 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, + 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, + 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, + 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, + 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, + 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, + 0xc8, 0x47, 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, + 0x7d, 0x60, 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, + 0x7c, 0x60, 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, + 0x30, 0x40, 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0xc8, 0x47, 0x7c, 0x60, + 0x38, 0x68, 0xc0, 0x46, 0xc0, 0x46, 0xc8, 0x47, 0x7d, 0x60, 0x30, 0x40, + 0x40, 0x04, 0x5b, 0x08, 0x03, 0x43, 0x00, 0xbd, 0x5f, 0x48, 0x83, 0x46, + 0x5f, 0x48, 0x82, 0x46, 0x5f, 0x48, 0x81, 0x46, 0x5f, 0x48, 0x80, 0x46, + 0x5f, 0x4f, 0x60, 0x4e, 0x60, 0x4d, 0x61, 0x4c, 0x70, 0x47, 0x03, 0x20, + 0x43, 0x46, 0xd8, 0x60, 0x00, 0xbd, 0x02, 0x20, 0x43, 0x46, 0xd8, 0x60, + 0x00, 0xbd, 0x00, 0xb5, 0x5c, 0x48, 0x84, 0x46, 0x5b, 0x68, 0xff, 0xf7, + 0xeb, 0xfc, 0x52, 0x4b, 0xbb, 0x60, 0xff, 0xf7, 0x79, 0xfe, 0x5b, 0x0f, + 0x01, 0x2b, 0x09, 0xd0, 0x57, 0x49, 0xb9, 0x60, 0x02, 0x2b, 0xe6, 0xd1, + 0x60, 0x46, 0x01, 0x38, 0x84, 0x46, 0xe6, 0xd0, 0x43, 0x46, 0xeb, 0xe7, + 0xff, 0xf7, 0x6c, 0xfe, 0xff, 0xf7, 0xbd, 0xfc, 0x50, 0x49, 0xb9, 0x60, + 0x59, 0x46, 0x31, 0x43, 0x79, 0x60, 0x41, 0x46, 0x4b, 0x60, 0x88, 0x60, + 0x00, 0x20, 0xc8, 0x60, 0x00, 0xbd, 0x00, 0xb5, 0x49, 0x48, 0x84, 0x46, + 0x5b, 0x68, 0xff, 0xf7, 0xc5, 0xfc, 0x08, 0xb4, 0x3e, 0x4b, 0xbb, 0x60, + 0xff, 0xf7, 0x52, 0xfe, 0x5b, 0x0f, 0x01, 0x2b, 0x0a, 0xd0, 0x01, 0xbc, + 0x43, 0x49, 0xb9, 0x60, 0x02, 0x2b, 0xbe, 0xd1, 0x60, 0x46, 0x01, 0x38, + 0x84, 0x46, 0xbe, 0xd0, 0x43, 0x46, 0xe9, 0xe7, 0x3e, 0x4b, 0xbb, 0x60, + 0xff, 0xf7, 0x32, 0xfe, 0x43, 0x46, 0x9b, 0x68, 0xff, 0xf7, 0xac, 0xfc, + 0x08, 0xbc, 0xff, 0xf7, 0x2b, 0xfe, 0x43, 0x46, 0x00, 0x20, 0xd8, 0x60, + 0x00, 0xbd, 0x00, 0xb5, 0x37, 0x4b, 0x9c, 0x46, 0xff, 0xf7, 0xa0, 0xfc, + 0x63, 0x46, 0xff, 0xf7, 0x9d, 0xfc, 0x35, 0x4b, 0xff, 0xf7, 0x98, 0xfc, + 0x63, 0x46, 0xff, 0xf7, 0x97, 0xfc, 0x63, 0x46, 0xff, 0xf7, 0x94, 0xfc, + 0x43, 0x46, 0x00, 0x20, 0xd8, 0x60, 0x00, 0xbd, 0x00, 0xb5, 0x2f, 0x48, + 0x00, 0x21, 0x01, 0x60, 0x43, 0x46, 0x18, 0x68, 0x05, 0x28, 0x00, 0xd9, + 0x00, 0x20, 0x80, 0x00, 0x01, 0xa1, 0x0a, 0x58, 0x90, 0x47, 0x00, 0xbd, + 0xf9, 0x07, 0x00, 0x18, 0xff, 0x07, 0x00, 0x18, 0xff, 0x06, 0x00, 0x18, + 0x4b, 0x07, 0x00, 0x18, 0x9b, 0x07, 0x00, 0x18, 0x05, 0x08, 0x00, 0x18, + 0x09, 0x20, 0xd8, 0x60, 0x70, 0x47, 0x00, 0x20, 0xd8, 0x60, 0x70, 0x47, + 0x58, 0x68, 0x08, 0x28, 0x00, 0xd9, 0x00, 0x20, 0x80, 0x00, 0x03, 0xa1, + 0x09, 0x58, 0x89, 0x46, 0x00, 0x20, 0xd8, 0x60, 0x70, 0x47, 0xc0, 0x46, + 0x65, 0x00, 0x00, 0x18, 0x65, 0x00, 0x00, 0x18, 0x65, 0x00, 0x00, 0x18, + 0x85, 0x00, 0x00, 0x18, 0x95, 0x00, 0x00, 0x18, 0x95, 0x00, 0x00, 0x18, + 0xa5, 0x00, 0x00, 0x18, 0xa5, 0x00, 0x00, 0x18, 0xad, 0x00, 0x00, 0x18, + 0x11, 0x48, 0x12, 0x49, 0x01, 0x60, 0xff, 0xf7, 0x45, 0xff, 0x11, 0x48, + 0x02, 0x21, 0x01, 0x60, 0x30, 0xbf, 0xfd, 0xe7, 0x00, 0x88, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x95, 0x00, 0x00, 0x18, 0x00, 0x40, 0x00, 0x18, + 0x10, 0x12, 0x10, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x9e, 0xe7, 0x00, 0x00, 0x30, 0x31, 0x04, 0x40, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xaa, 0xaa, 0x00, 0xe1, 0x00, 0xe0 +}; +unsigned int zero_bin_len = 2196; diff --git a/app/mdebug/mdebug.c b/app/mdebug/mdebug.c new file mode 100644 index 00000000..f186f39f --- /dev/null +++ b/app/mdebug/mdebug.c @@ -0,0 +1,160 @@ +/* mdebug.c + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <app.h> +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> +#include <dev/udc.h> + +#include <platform.h> +#include <arch/arm.h> +#include <kernel/thread.h> +#include <kernel/event.h> + +#include <platform/lpc43xx-gpio.h> + +#include "swd.h" + +#define TX_AHEAD 1 + +static event_t txevt = EVENT_INITIAL_VALUE(txevt, TX_AHEAD, 0); +static event_t rxevt = EVENT_INITIAL_VALUE(rxevt, 0, 0); + +static udc_request_t *txreq; +static udc_request_t *rxreq; +static udc_endpoint_t *txept; +static udc_endpoint_t *rxept; + +static volatile int online; +static volatile int txstatus; +static volatile int rxstatus; +static volatile int rxactual; + +static void mdebug_notify(udc_gadget_t *gadget, unsigned event) { + if (event == UDC_EVENT_ONLINE) { + online = 1; + } else { + online = 0; + } +} + +static void rx_complete(udc_request_t *req, unsigned actual, int status) { + rxactual = actual; + rxstatus = status; + event_signal(&rxevt, 0); +} + +static void tx_complete(udc_request_t *req, unsigned actual, int status) { + txstatus = status; + event_signal(&txevt, 0); +} + +#if TX_AHEAD +void usb_xmit(void *data, unsigned len) { + event_wait(&txevt); + event_unsignal(&txevt); + txreq->buffer = data; + txreq->length = len; + txstatus = 1; + if (udc_request_queue(txept, txreq)) { + printf("txqf\n"); + event_signal(&txevt, 0); + } +} +#else +void usb_xmit(void *data, unsigned len) { + event_unsignal(&txevt); + txreq->buffer = data; + txreq->length = len; + txstatus = 1; + if (udc_request_queue(txept, txreq) == 0) { + event_wait(&txevt); + } +} +#endif + +int usb_recv(void *data, unsigned len) { + event_unsignal(&rxevt); + rxreq->buffer = data; + rxreq->length = len; + rxstatus = 1; + if (udc_request_queue(rxept, rxreq)) { + printf("rxqf\n"); + return -1; + } + event_wait(&rxevt); + return rxstatus ? rxstatus : rxactual; +} + +static udc_device_t mdebug_device = { + .vendor_id = 0x1209, + .product_id = 0x5038, + .version_id = 0x0100, +}; + +static udc_endpoint_t *mdebug_endpoints[2]; + +static udc_gadget_t mdebug_gadget = { + .notify = mdebug_notify, + .ifc_class = 0xFF, + .ifc_subclass = 0xFF, + .ifc_protocol = 0xFF, + .ifc_endpoints = 2, + .ept = mdebug_endpoints, +}; + +static void mdebug_init(const struct app_descriptor *app) +{ + swd_init(); + + udc_init(&mdebug_device); + mdebug_endpoints[0] = txept = udc_endpoint_alloc(UDC_BULK_IN, 512); + mdebug_endpoints[1] = rxept = udc_endpoint_alloc(UDC_BULK_OUT, 512); + txreq = udc_request_alloc(); + rxreq = udc_request_alloc(); + rxreq->complete = rx_complete; + txreq->complete = tx_complete; + udc_register_gadget(&mdebug_gadget); +} + +void handle_rswd(void); +void swo_init(udc_endpoint_t *ept); +void swo_config(unsigned mhz); + +static void mdebug_entry(const struct app_descriptor *app, void *args) +{ + udc_start(); + swo_init(txept); + swo_config(6000000); + + for (;;) { + if (!online) { + thread_yield(); + continue; + } + handle_rswd(); + } +} + +APP_START(usbtest) + .init = mdebug_init, + .entry = mdebug_entry, +APP_END + + diff --git a/app/mdebug/rswd.c b/app/mdebug/rswd.c new file mode 100644 index 00000000..d335514a --- /dev/null +++ b/app/mdebug/rswd.c @@ -0,0 +1,247 @@ +/* rswd.c + * + * Copyright 2011-2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <reg.h> +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> + +#include <platform.h> + +#include "swd.h" +#include "rswdp.h" + +void usb_xmit(void *data, unsigned len); +int usb_recv(void *data, unsigned len); + +unsigned swdp_trace = 0; + +// indicates host knows about v1.0 protocol features +unsigned host_version = 0; + +static u8 optable[16] = { + [OP_RD | OP_DP | OP_X0] = RD_IDCODE, + [OP_RD | OP_DP | OP_X4] = RD_DPCTRL, + [OP_RD | OP_DP | OP_X8] = RD_RESEND, + [OP_RD | OP_DP | OP_XC] = RD_BUFFER, + [OP_WR | OP_DP | OP_X0] = WR_ABORT, + [OP_WR | OP_DP | OP_X4] = WR_DPCTRL, + [OP_WR | OP_DP | OP_X8] = WR_SELECT, + [OP_WR | OP_DP | OP_XC] = WR_BUFFER, + [OP_RD | OP_AP | OP_X0] = RD_AP0, + [OP_RD | OP_AP | OP_X4] = RD_AP1, + [OP_RD | OP_AP | OP_X8] = RD_AP2, + [OP_RD | OP_AP | OP_XC] = RD_AP3, + [OP_WR | OP_AP | OP_X0] = WR_AP0, + [OP_WR | OP_AP | OP_X4] = WR_AP1, + [OP_WR | OP_AP | OP_X8] = WR_AP2, + [OP_WR | OP_AP | OP_XC] = WR_AP3, +}; + +static const char *board_str = TARGET; +static const char *build_str = "fw v0.91 (" __DATE__ ", " __TIME__ ")"; + +static void _reboot(void) { + platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET); +} + +/* TODO bounds checking -- we trust the host far too much */ +void process_txn(u32 txnid, u32 *rx, int rxc, u32 *tx) { + unsigned msg, op, n; + unsigned txc = 1; + unsigned count = 0; + unsigned status = 0; + void (*func)(void) = 0; + + tx[0] = txnid; + + while (rxc-- > 0) { + count++; + msg = *rx++; + op = RSWD_MSG_OP(msg); + n = RSWD_MSG_ARG(msg); +#if CONFIG_MDEBUG_TRACE + printf("> %02x %02x %04x <\n", RSWD_MSG_CMD(msg), op, n); +#endif + switch (RSWD_MSG_CMD(msg)) { + case CMD_NULL: + continue; + case CMD_SWD_WRITE: + while (n-- > 0) { + rxc--; + status = swd_write(optable[op], *rx++); + if (status) { + goto done; + } + } + continue; + case CMD_SWD_READ: + tx[txc++] = RSWD_MSG(CMD_SWD_DATA, 0, n); + while (n-- > 0) { + status = swd_read(optable[op], tx + txc); + if (status) { + txc++; + while (n-- > 0) + tx[txc++] = 0xfefefefe; + goto done; + } + txc++; + } + continue; + case CMD_SWD_DISCARD: + while (n-- > 0) { + u32 tmp; + status = swd_read(optable[op], &tmp); + if (status) { + goto done; + } + } + continue; + case CMD_ATTACH: + swd_reset(); + continue; + case CMD_RESET: + swd_hw_reset(n); + continue; + case CMD_DOWNLOAD: { + //u32 *addr = (void*) *rx++; + rxc--; + while (n) { + //*addr++ = *rx++; + rx++; + rxc--; + } + continue; + } + case CMD_EXECUTE: + //func = (void*) *rx++; + rxc--; + continue; + case CMD_TRACE: + swdp_trace = op; + continue; + case CMD_BOOTLOADER: + func = _reboot; + continue; + case CMD_SET_CLOCK: + n = swd_set_clock(n); + printf("swdp clock is now %d KHz\n", n); + if (host_version >= RSWD_VERSION_1_0) { + tx[txc++] = RSWD_MSG(CMD_CLOCK_KHZ, 0, n); + } + continue; + case CMD_SWO_CLOCK: + n = swo_set_clock(n); + printf("swo clock is now %d KHz\n", n); + continue; + case CMD_VERSION: + host_version = n; + tx[txc++] = RSWD_MSG(CMD_VERSION, 0, RSWD_VERSION); + + n = strlen(board_str); + memcpy(tx + txc + 1, board_str, n + 1); + n = (n + 4) / 4; + tx[txc++] = RSWD_MSG(CMD_BOARD_STR, 0, n); + txc += n; + + n = strlen(build_str); + memcpy(tx + txc + 1, build_str, n + 1); + n = (n + 4) / 4; + tx[txc++] = RSWD_MSG(CMD_BUILD_STR, 0, n); + txc += n; + + tx[txc++] = RSWD_MSG(CMD_RX_MAXDATA, 0, 8192); + txc += n; + continue; + default: + printf("unknown command %02x\n", RSWD_MSG_CMD(msg)); + status = 1; + goto done; + } + } + +done: + tx[txc++] = RSWD_MSG(CMD_STATUS, status, count); + + /* if we're about to send an even multiple of the packet size + * (64), add a NULL op on the end to create a short packet at + * the end. + */ + if ((txc & 0xf) == 0) + tx[txc++] = RSWD_MSG(CMD_NULL, 0, 0); + +#if CONFIG_MDEBUG_TRACE + printf("[ send %d words ]\n", txc); + for (n = 0; n < txc; n+=4) { + printx("%08x %08x %08x %08x\n", + tx[n], tx[n+1], tx[n+2], tx[n+3]); + } +#endif + usb_xmit(tx, txc * 4); + + if (func) { + for (n = 0; n < 1000000; n++) asm("nop"); + func(); + for (;;) ; + } +} + +// io buffers in AHB SRAM +static u32 *rxbuffer = (void*) 0x20001000; +static u32 *txbuffer[2] = {(void*) 0x20003000, (void*) 0x20005000 }; + +#include <kernel/thread.h> + +void handle_rswd(void) { + int rxc; + int toggle = 0; + +#if CONFIG_MDEBUG_TRACE + printf("[ rswdp agent v0.9 ]\n"); + printf("[ built " __DATE__ " " __TIME__ " ]\n"); +#endif + + for (;;) { + rxc = usb_recv(rxbuffer, 8192); + +#if CONFIG_MDEBUG_TRACE + int n; + printx("[ recv %d words ]\n", rxc/4); + for (n = 0; n < (rxc/4); n+=4) { + printx("%08x %08x %08x %08x\n", + rxbuffer[n], rxbuffer[n+1], + rxbuffer[n+2], rxbuffer[n+3]); + } +#endif + + if ((rxc < 4) || (rxc & 3)) { + printf("error, runt frame, or strange frame... %d\n", rxc); + continue; + } + + rxc = rxc / 4; + + if ((rxbuffer[0] & 0xFFFF0000) != 0xAA770000) { + printf("invalid frame %x\n", rxbuffer[0]); + continue; + } + + process_txn(rxbuffer[0], rxbuffer + 1, rxc - 1, txbuffer[toggle]); + toggle ^= 1; + } +} diff --git a/app/mdebug/rswdp.h b/app/mdebug/rswdp.h new file mode 100644 index 00000000..8c20ae6e --- /dev/null +++ b/app/mdebug/rswdp.h @@ -0,0 +1,116 @@ +/* rswdp.h - remote serial wire debug protocol + * + * Copyright 2011 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Remote Serial Wire Debug Protocol */ + +#ifndef _RSWDP_PROTOCOL_H_ +#define _RSWDP_PROTOCOL_H_ + +/* Basic framing: + * - host and device exchange "transactions" consisting of + * some number of "messages". + * - each "message" has a 32bit header and may have 0 or more + * 32bit words of payload + * - a transaction may not exceed 4K (1024 words) + * - a transaction is sent in a series of USB BULK packets + * - the final packet must be a short packet unless the + * transaction is exactly 4K in length + * - packets must be a multiple of 4 bytes + * - the first message in a transaction must be + * CMD_TXN_START or CMD_TXN_ASYNC + */ + +#define RSWD_MSG(cmd,op,n) ((((cmd)&0xFF) << 24) | (((op) & 0xFF)<<16) | ((n) & 0xFFFF)) +#define RSWD_MSG_CMD(n) (((n) >> 24) & 0xFF) +#define RSWD_MSG_OP(n) (((n) >> 16) & 0xFF) +#define RSWD_MSG_ARG(n) ((n) & 0xFFFF) + +#define RSWD_TXN_START(seq) (0xAA770000 | ((seq) & 0xFFFF)) +#define RSWD_TXN_ASYNC (0xAA001111) + +/* valid: either */ +#define CMD_NULL 0x00 /* used for padding */ + +/* valid: host to target */ +#define CMD_SWD_WRITE 0x01 /* op=addr arg=count payload: data x count */ +#define CMD_SWD_READ 0x02 /* op=addr arg=count payload: data x count */ +#define CMD_SWD_DISCARD 0x03 /* op=addr arg=count payload: none (discards) */ +#define CMD_ATTACH 0x04 /* do swdp reset/connect handshake */ +#define CMD_RESET 0x05 /* arg=1 -> assert RESETn, otherwise deassert */ +#define CMD_DOWNLOAD 0x06 /* arg=wordcount, payload: addr x 1, data x n */ +#define CMD_EXECUTE 0x07 /* payload: addr x 1 */ +#define CMD_TRACE 0x08 /* op=tracebits n=0 */ +#define CMD_BOOTLOADER 0x09 /* return to bootloader for reflashing */ +#define CMD_SET_CLOCK 0x0A /* set SWCLK rate to n khz */ +#define CMD_SWO_CLOCK 0x0B /* set SWOCLK rate to n khz, 0 = disable SWO */ + +/* valid: target to host */ +#define CMD_STATUS 0x10 /* op=errorcode, arg=commands since last TXN_START */ +#define CMD_SWD_DATA 0x11 /* op=0 arg=count, payload: data x count */ +#define CMD_SWO_DATA 0x12 /* op=0 arg=bytecount, payload: ((arg + 3) / 4) words*/ + +/* valid: target to host async */ +#define CMD_DEBUG_PRINT 0x20 /* arg*4 bytes of ascii debug output */ + +/* valid: bidirectional query/config messages */ +#define CMD_VERSION 0x30 /* arg=bcdversion (0x0100 etc) */ +#define CMD_BUILD_STR 0x31 /* arg=wordcount, payload = asciiz */ +#define CMD_BOARD_STR 0x32 /* arg=wordcount, payload = asciiz */ +#define CMD_RX_MAXDATA 0x33 /* arg=bytes, declares senders rx buffer size */ +#define CMD_CLOCK_KHZ 0x34 /* arg=khz, reports active clock rate */ + +/* CMD_STATUS error codes */ +#define ERR_NONE 0 +#define ERR_INTERNAL 1 +#define ERR_TIMEOUT 2 +#define ERR_IO 3 +#define ERR_PARITY 4 + +#define RSWD_VERSION 0x0101 + +#define RSWD_VERSION_1_0 0x0100 +#define RSWD_VERSION_1_1 0x0101 + +// Pre-1.0 +// - max packet size fixed at 2048 bytes +// +// Version 1.0 +// - CMD_VERSION, CMD_BUILD_STR, CMD_BOARD_STR, CMD_RX_MAXDATA, +// CMD_CLOCK_KHZ added +// +// Version 1.1 +// - CMD_SWO_DATA arg is now byte count, not word count + +/* CMD_SWD_OP operations - combine for direct AP/DP io */ +#define OP_RD 0x00 +#define OP_WR 0x01 +#define OP_DP 0x00 +#define OP_AP 0x02 +#define OP_X0 0x00 +#define OP_X4 0x04 +#define OP_X8 0x08 +#define OP_XC 0x0C + +/* DP registers */ +#define DP_IDCODE (OP_DP|OP_X0) +#define DP_ABORT (OP_DP|OP_X0) +#define DP_DPCTRL (OP_DP|OP_X4) +#define DP_RESEND (OP_DP|OP_X8) +#define DP_SELECT (OP_DP|OP_X8) +#define DP_BUFFER (OP_DP|OP_XC) + +#endif diff --git a/app/mdebug/rules.mk b/app/mdebug/rules.mk new file mode 100644 index 00000000..4a70efd6 --- /dev/null +++ b/app/mdebug/rules.mk @@ -0,0 +1,14 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/mdebug.c \ + $(LOCAL_DIR)/rswd.c \ + $(LOCAL_DIR)/swo-uart1.c + +#MODULE_SRCS += $(LOCAL_DIR)/swd-sgpio.c +MODULE_SRCS += $(LOCAL_DIR)/swd-m0sub.c + +include make/module.mk + diff --git a/app/mdebug/swd-m0sub.c b/app/mdebug/swd-m0sub.c new file mode 100644 index 00000000..de3ec9d2 --- /dev/null +++ b/app/mdebug/swd-m0sub.c @@ -0,0 +1,197 @@ +/* swdp-m0sub.c + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <app.h> +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> + +#include <platform.h> +#include <arch/arm.h> +#include <kernel/thread.h> + +#include <platform/lpc43xx-gpio.h> +#include <platform/lpc43xx-sgpio.h> +#include <platform/lpc43xx-clocks.h> + +#include "rswdp.h" + +#define PIN_LED PIN(1,1) +#define PIN_RESET PIN(2,5) +#define PIN_RESET_TXEN PIN(2,6) +#define PIN_SWDIO_TXEN PIN(1,5) // SGPIO15=6 +#define PIN_SWDIO PIN(1,6) // SGPIO14=6 +#define PIN_SWO PIN(1,14) // U1_RXD=1 +#define PIN_SWCLK PIN(1,17) // SGPIO11=6 + +#define GPIO_LED GPIO(0,8) +#define GPIO_RESET GPIO(5,5) +#define GPIO_RESET_TXEN GPIO(5,6) +#define GPIO_SWDIO_TXEN GPIO(1,8) +#define GPIO_SWDIO GPIO(1,9) +#define GPIO_SWCLK GPIO(0,12) + +static void gpio_init(void) { + pin_config(PIN_LED, PIN_MODE(0) | PIN_PLAIN); + pin_config(PIN_RESET, PIN_MODE(4) | PIN_PLAIN); + pin_config(PIN_RESET_TXEN, PIN_MODE(4) | PIN_PLAIN); + + pin_config(PIN_SWDIO_TXEN, PIN_MODE(6) | PIN_PLAIN | PIN_FAST); + pin_config(PIN_SWDIO, PIN_MODE(6) | PIN_PLAIN | PIN_INPUT | PIN_FAST); + pin_config(PIN_SWCLK, PIN_MODE(6) | PIN_PLAIN | PIN_FAST); + + pin_config(PIN_SWO, PIN_MODE(1) | PIN_PLAIN | PIN_INPUT | PIN_FAST); + + gpio_set(GPIO_LED, 0); + gpio_set(GPIO_RESET, 1); + gpio_set(GPIO_RESET_TXEN, 0); + + gpio_config(GPIO_LED, GPIO_OUTPUT); + gpio_config(GPIO_RESET, GPIO_OUTPUT); + gpio_config(GPIO_RESET_TXEN, GPIO_OUTPUT); +} + + +/* returns 1 if the number of bits set in n is odd */ +static unsigned parity(unsigned n) { + n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); + n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); + n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); + n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); + n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); + return n & 1; +} + +#include "fw-m0sub.h" + +#define M0SUB_ZEROMAP 0x40043308 +#define M0SUB_TXEV 0x40043314 // write 0 to clear +#define M4_TXEV 0x40043130 // write 0 to clear + +#define RESET_CTRL0 0x40053100 +#define M0_SUB_RST (1 << 12) + +#define COMM_CMD 0x18004000 +#define COMM_ARG1 0x18004004 +#define COMM_ARG2 0x18004008 +#define COMM_RESP 0x1800400C + +#define CMD_ERR 0 +#define CMD_NOP 1 +#define CMD_READ 2 +#define CMD_WRITE 3 +#define CMD_RESET 4 +#define CMD_SETCLOCK 5 + +#define RSP_BUSY 0xFFFFFFFF + +void swd_init(void) { + gpio_init(); + + writel(BASE_CLK_SEL(CLK_PLL1), BASE_PERIPH_CLK); + spin(1000); + + // SGPIO15 SWDIO_TXEN + writel(CFG_OUT_GPIO | CFG_OE_GPIO, SGPIO_OUT_CFG(15)); + // SGPIO14 SWDIO + writel(CFG_OUT_GPIO | CFG_OE_GPIO, SGPIO_OUT_CFG(14)); + // SGPIO11 SWCLK + writel(CFG_OUT_GPIO | CFG_OE_GPIO, SGPIO_OUT_CFG(11)); + + // all outputs enabled and high + writel((1 << 11) | (1 << 14) | (1 << 15), SGPIO_OUT); + writel((1 << 11) | (1 << 14) | (1 << 15), SGPIO_OEN); + + writel(0, M4_TXEV); + writel(M0_SUB_RST, RESET_CTRL0); + writel(0x18000000, M0SUB_ZEROMAP); + writel(0xffffffff, 0x18004000); + memcpy((void*) 0x18000000, zero_bin, sizeof(zero_bin)); + DSB; + writel(0, RESET_CTRL0); +} + +int swd_write(unsigned hdr, unsigned data) { + unsigned n; + unsigned p = parity(data); + writel(CMD_WRITE, COMM_CMD); + writel((hdr << 8) | (p << 16), COMM_ARG1); + writel(data, COMM_ARG2); + writel(RSP_BUSY, COMM_RESP); + DSB; + asm("sev"); + while ((n = readl(COMM_RESP)) == RSP_BUSY) ; + //printf("wr s=%d\n", n); + return n; +} + +int swd_read(unsigned hdr, unsigned *val) { + unsigned n, data, p; + writel(CMD_READ, COMM_CMD); + writel(hdr << 8, COMM_ARG1); + writel(RSP_BUSY, COMM_RESP); + DSB; + asm("sev"); + while ((n = readl(COMM_RESP)) == RSP_BUSY) ; + if (n) { + return n; + } + data = readl(COMM_ARG1); + p = readl(COMM_ARG2); + if (p != parity(data)) { + return ERR_PARITY; + } + //printf("rd s=%d p=%d d=%08x\n", n, p, data); + *val = data; + return 0; +} + +void swd_reset(void) { + unsigned n; + writel(CMD_RESET, COMM_CMD); + writel(RSP_BUSY, COMM_RESP); + DSB; + asm("sev"); + while ((n = readl(COMM_RESP)) == RSP_BUSY) ; +} + +unsigned swd_set_clock(unsigned khz) { + unsigned n; + if (khz > 8000) { + khz = 8000; + } + writel(CMD_SETCLOCK, COMM_CMD); + writel(khz/1000, COMM_ARG1); + writel(RSP_BUSY, COMM_RESP); + DSB; + asm("sev"); + while ((n = readl(COMM_RESP)) == RSP_BUSY) ; + + // todo: accurate value + return khz; +} + +void swd_hw_reset(int assert) { + if (assert) { + gpio_set(GPIO_RESET, 0); + gpio_set(GPIO_RESET_TXEN, 1); + } else { + gpio_set(GPIO_RESET, 1); + gpio_set(GPIO_RESET_TXEN, 0); + } +} diff --git a/app/mdebug/swd-sgpio.c b/app/mdebug/swd-sgpio.c new file mode 100644 index 00000000..52213874 --- /dev/null +++ b/app/mdebug/swd-sgpio.c @@ -0,0 +1,366 @@ +/* swdp-sgpio.c + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <app.h> +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> +#include <dev/udc.h> + +#include <platform.h> +#include <arch/arm.h> +#include <kernel/thread.h> + +#include <platform/lpc43xx-gpio.h> +#include <platform/lpc43xx-sgpio.h> +#include <platform/lpc43xx-clocks.h> + +#define PIN_LED PIN(1,1) +#define PIN_RESET PIN(2,5) +#define PIN_RESET_TXEN PIN(2,6) +#define PIN_SWDIO_TXEN PIN(1,5) // SGPIO15=6 +#define PIN_SWDIO PIN(1,6) // SGPIO14=6 +#define PIN_SWO PIN(1,14) // U1_RXD=1 +#define PIN_SWCLK PIN(1,17) // SGPIO11=6 + +#define GPIO_LED GPIO(0,8) +#define GPIO_RESET GPIO(5,5) +#define GPIO_RESET_TXEN GPIO(5,6) +#define GPIO_SWDIO_TXEN GPIO(1,8) +#define GPIO_SWDIO GPIO(1,9) +#define GPIO_SWCLK GPIO(0,12) + +static unsigned sgpio_div = 31; // 6MHz + +static void gpio_init(void) { + pin_config(PIN_LED, PIN_MODE(0) | PIN_PLAIN); + pin_config(PIN_RESET, PIN_MODE(4) | PIN_PLAIN); + pin_config(PIN_RESET_TXEN, PIN_MODE(4) | PIN_PLAIN); + pin_config(PIN_SWDIO_TXEN, PIN_MODE(0) | PIN_PLAIN); + pin_config(PIN_SWDIO, PIN_MODE(0) | PIN_PLAIN | PIN_INPUT | PIN_FAST); + pin_config(PIN_SWCLK, PIN_MODE(0) | PIN_PLAIN | PIN_FAST); + pin_config(PIN_SWO, PIN_MODE(1) | PIN_PLAIN | PIN_INPUT | PIN_FAST); + + gpio_set(GPIO_LED, 0); + gpio_set(GPIO_RESET, 1); + gpio_set(GPIO_RESET_TXEN, 0); + gpio_set(GPIO_SWDIO, 0); + gpio_set(GPIO_SWDIO_TXEN, 1); + gpio_set(GPIO_SWCLK, 0); + + gpio_config(GPIO_LED, GPIO_OUTPUT); + gpio_config(GPIO_RESET, GPIO_OUTPUT); + gpio_config(GPIO_RESET_TXEN, GPIO_OUTPUT); + gpio_config(GPIO_SWDIO, GPIO_OUTPUT); + gpio_config(GPIO_SWDIO_TXEN, GPIO_OUTPUT); + gpio_config(GPIO_SWCLK, GPIO_OUTPUT); +} + +/* returns 1 if the number of bits set in n is odd */ +static unsigned parity(unsigned n) { + n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); + n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); + n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); + n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); + n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); + return n & 1; +} + +static void sgpio_txn(unsigned slices) { + // clear any previous status bits + writel(0xFFFF, SLICE_XHG_STS_CLR); + // kick the txn + writel(slices, SLICE_CTRL_ENABLE); + writel(slices, SLICE_CTRL_DISABLE); + // wait for all slices to complete + while ((readl(SLICE_XHG_STS) & slices) != slices) ; + // shut down clocks + writel(0, SLICE_CTRL_ENABLE); + writel(0, SLICE_CTRL_DISABLE); +} + +// SWDIO SLICE_H SLICE_P SLICE_D SLICE_O SWDIO +// SGPIO14 -> [31....0] -> [31....0] [31....0] -> [31....0] -> SGPIO14 +// +// SLICE_M SWDIO_TXEN +// [31....7] -> SGPIO15 +// +// SLICE_F SWDIO_OE +// [31....0] -> SGPIO14_OE + + +// configures all slices, muxes, etc +// ensures that outputs are enabled and SWDIO and SWCLK are high +static void sgpio_init(void) { + writel(BASE_CLK_SEL(CLK_PLL1), BASE_PERIPH_CLK); + + // make sure everything's shut down + writel(0, SLICE_CTRL_ENABLE); + writel(0, SLICE_CTRL_DISABLE); + writel(0xFFFF, SLICE_XHG_STS_CLR); + + // SWDIO_TXEN (SGPIO15) + // M[31..7] -> OUT + writel(CFG_OUT_M8B | CFG_OE_GPIO, SGPIO_OUT_CFG(15)); + writel(CLK_USE_SLICE | QUAL_ENABLE | CONCAT_SLICE, SLICE_CFG1(SLC_M)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC, SLICE_CFG2(SLC_M)); + + // SWDIO (SGPIO14) + // IN -> H[31..0] -> P[31..0] + // D[31..0] -> O[31..0] -> OUT + // F[31..0] -> OE + writel(CFG_OUT_M2C | CFG_OE_M1, SGPIO_OUT_CFG(14)); + writel(CLK_USE_SLICE | QUAL_ENABLE | CONCAT_PIN, SLICE_CFG1(SLC_H)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC, SLICE_CFG2(SLC_H)); + writel(CLK_USE_SLICE | QUAL_ENABLE | CONCAT_SLICE | CONCAT_2_SLICE, SLICE_CFG1(SLC_P)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC, SLICE_CFG2(SLC_P)); + writel(CLK_USE_SLICE | QUAL_ENABLE | CONCAT_SLICE, SLICE_CFG1(SLC_D)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC, SLICE_CFG2(SLC_D)); + writel(CLK_USE_SLICE | QUAL_ENABLE | CONCAT_SLICE | CONCAT_2_SLICE, SLICE_CFG1(SLC_O)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC, SLICE_CFG2(SLC_O)); + writel(CLK_USE_SLICE | QUAL_ENABLE | CONCAT_SLICE, SLICE_CFG1(SLC_F)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC, SLICE_CFG2(SLC_F)); + + // SWDCLK (SGPIO11) + // SLICE_N CLK -> OUT + writel(CFG_OUT_CLK | CFG_OE_GPIO, SGPIO_OUT_CFG(11)); + writel(CLK_USE_SLICE | QUAL_ENABLE, SLICE_CFG1(SLC_N)); + writel(CLK_GEN_INTERNAL | SHIFT_1BPC | INV_CLK_OUT, SLICE_CFG2(SLC_N)); + + // ensure output and enables idle high + writel(1, SLICE_REG(SLC_F)); + writel(1, SLICE_REG(SLC_O)); + writel(1 << 7, SLICE_REG(SLC_M)); + + // enable all outputs + writel((1 << 11) | (1 << 14) | (1 << 15), SGPIO_OEN); + + // select SGPIOs via pin mux + pin_config(PIN_SWDIO_TXEN, PIN_MODE(6) | PIN_PLAIN | PIN_FAST); + pin_config(PIN_SWDIO, PIN_MODE(6) | PIN_PLAIN | PIN_INPUT | PIN_FAST); + pin_config(PIN_SWCLK, PIN_MODE(6) | PIN_PLAIN | PIN_FAST); +} + +static void sgpio_swd_clock_setup(unsigned div) { + writel(div, SLICE_PRESET(SLC_D)); + writel(div, SLICE_PRESET(SLC_F)); + writel(div, SLICE_PRESET(SLC_H)); + writel(div, SLICE_PRESET(SLC_M)); + writel(div, SLICE_PRESET(SLC_N)); + writel(div, SLICE_PRESET(SLC_O)); + writel(div, SLICE_PRESET(SLC_P)); +} + +static void sgpio_swd_reset(unsigned div) { + // shift out 64 clocks while DATA is 1 + writel(0, SLICE_COUNT(SLC_N)); + writel(POS_POS(63) | POS_RESET(63), SLICE_POS(SLC_N)); + sgpio_txn(1 << SLC_N); + + // shift out 16bit jtag->swd escape pattern + writel(0, SLICE_COUNT(SLC_N)); + writel(POS_POS(15) | POS_RESET(15), SLICE_POS(SLC_N)); + + writel(0b1110011110011110, SLICE_REG(SLC_O)); + writel(1, SLICE_SHADOW(SLC_O)); + writel(div, SLICE_COUNT(SLC_O)); + writel(POS_POS(15) | POS_RESET(15), SLICE_POS(SLC_O)); + sgpio_txn((1 << SLC_N) | (1 << SLC_O)); + + // shift out 64 clocks while DATA is 1 + writel(0, SLICE_COUNT(SLC_N)); + writel(POS_POS(63) | POS_RESET(63), SLICE_POS(SLC_N)); + sgpio_txn(1 << SLC_N); +}; + +// shift out 8 0s then the 8bit header, then disable outputs +// and shift in the turnaround bit (ignored) and 3 bit ack +// leaves output enables low +// +// todo: make leader optional/adjustable +static int sgpio_swd_header(unsigned div, uint32_t hdr) { + unsigned timeout = 16; + unsigned ack; + + for (;;) { + // 16 bits tx_en, then stop, disabling tx_en + writel(0xFFFF << 7, SLICE_REG(SLC_M)); + writel(0, SLICE_SHADOW(SLC_M)); + writel(div, SLICE_COUNT(SLC_M)); + writel(POS_POS(15) | POS_RESET(15), SLICE_POS(SLC_M)); + + // 16 bits output, then stop, disabling OE + writel(0xFFFF, SLICE_REG(SLC_F)); + writel(0, SLICE_SHADOW(SLC_F)); + writel(div, SLICE_COUNT(SLC_F)); + writel(POS_POS(15) | POS_RESET(15), SLICE_POS(SLC_F)); + + // 16 bits data out + writel(hdr << 8, SLICE_REG(SLC_O)); + writel(1, SLICE_SHADOW(SLC_O)); + writel(div, SLICE_COUNT(SLC_O)); + writel(POS_POS(15) | POS_RESET(15), SLICE_POS(SLC_O)); + + // 20 bits data in + writel(0, SLICE_COUNT(SLC_H)); + writel(POS_POS(19) | POS_RESET(19), SLICE_POS(SLC_H)); + writel(0, SLICE_REG(SLC_H)); + + // 20 bits clock + writel(0, SLICE_COUNT(SLC_N)); + writel(POS_POS(19) | POS_RESET(19), SLICE_POS(SLC_N)); + + sgpio_txn((1<<SLC_M)|(1<<SLC_F)|(1<<SLC_O)|(1<<SLC_H)|(1<<SLC_N)); + + if ((ack = readl(SLICE_SHADOW(SLC_H)) >> 29) == 1) { + // OKAY + if (timeout < 16) printf("[%d]\n",16-timeout); + return 0; + } + + // re-enable oe, tx_en, and make data high + writel(1, SLICE_REG(SLC_O)); + writel(1 << 7, SLICE_REG(SLC_M)); + writel(1, SLICE_REG(SLC_F)); + + // technically we should do a Turn cycle here, + // but we rely on the fact that we prefix all ops + // with some leader 0s and can be lazy + + if (ack == 2) { + // WAIT + if (timeout == 0) { + return -1; + } + timeout--; + } else { + printf("ERR %d\n", ack); + // FAULT or invalid response + return -1; + } + } +} + +static int sgpio_swd_read(unsigned div, uint32_t hdr, uint32_t *_data) { + uint32_t data, p; + + //printf("rd(%d,%02x)\n", div, hdr); + if (sgpio_swd_header(div, hdr)) { + return -1; + } + + // 34 bits in -> H -> P + writel(0, SLICE_COUNT(SLC_H)); + writel(POS_POS(33) | POS_RESET(33), SLICE_POS(SLC_H)); + writel(0, SLICE_REG(SLC_H)); + writel(0, SLICE_COUNT(SLC_P)); + writel(POS_POS(33) | POS_RESET(33), SLICE_POS(SLC_P)); + writel(0, SLICE_REG(SLC_P)); + + writel(0, SLICE_COUNT(SLC_N)); + writel(POS_POS(33) | POS_RESET(33), SLICE_POS(SLC_N)); + sgpio_txn((1<<SLC_H)|(1<<SLC_P)|(1<<SLC_N)); + + // re-enable oe, tx_en, and make data high + writel(1, SLICE_REG(SLC_O)); + writel(1 << 7, SLICE_REG(SLC_M)); + writel(1, SLICE_REG(SLC_F)); + + p = readl(SLICE_SHADOW(SLC_H)); + data = (p << 2) | (readl(SLICE_SHADOW(SLC_P)) >> 30); + p = (p >> 30) & 1; + + //printf("RD %08x %d\n", data, p); + if (parity(data) != p) { + printf("parity error\n"); + return -1; + } + *_data = data; + return 0; +} + +static int sgpio_swd_write(unsigned div, uint32_t hdr, uint32_t data) { + uint32_t p = parity(data); + + //printf("wr(%d,%02x,%08x) p=%d\n", div, hdr, data, p); + if (sgpio_swd_header(div, hdr)) { + return -1; + } + + // 34 bits D -> O -> out + writel(div, SLICE_COUNT(SLC_D)); + writel(POS_POS(33) | POS_RESET(33), SLICE_POS(SLC_D)); + writel((p << 1) | (data >> 31), SLICE_REG(SLC_D)); + writel(div, SLICE_COUNT(SLC_O)); + writel(POS_POS(33) | POS_RESET(33), SLICE_POS(SLC_O)); + writel((data << 1) | 1, SLICE_REG(SLC_O)); + + writel(0, SLICE_COUNT(SLC_N)); + writel(POS_POS(33) | POS_RESET(33), SLICE_POS(SLC_N)); + + // re-enable oe, tx_en + writel(1 << 7, SLICE_REG(SLC_M)); + writel(1, SLICE_REG(SLC_F)); + + sgpio_txn((1<<SLC_D)|(1<<SLC_O)|(1<<SLC_N)); + + return 0; +} + +void swd_init(void) { + gpio_init(); + sgpio_init(); +} + +void swd_reset(void) { + unsigned div = sgpio_div; + sgpio_swd_clock_setup(div); + sgpio_swd_reset(div); +} + +int swd_read(unsigned reg, unsigned *val) { + unsigned div = sgpio_div; + sgpio_swd_clock_setup(div); + return sgpio_swd_read(div, reg, val); +} + +int swd_write(unsigned reg, unsigned val) { + unsigned div = sgpio_div; + sgpio_swd_clock_setup(div); + return sgpio_swd_write(div, reg, val); +} + +unsigned swd_set_clock(unsigned khz) { + unsigned div; + if (khz < 2000) khz = 2000; + if (khz > 48000) khz = 48000; + div = 192000 / khz; + sgpio_div = div - 1; + return 192000 / div; +} + +void swd_hw_reset(int assert) { + if (assert) { + gpio_set(GPIO_RESET, 0); + gpio_set(GPIO_RESET_TXEN, 1); + } else { + gpio_set(GPIO_RESET, 1); + gpio_set(GPIO_RESET_TXEN, 0); + } +} + diff --git a/app/mdebug/swd.h b/app/mdebug/swd.h new file mode 100644 index 00000000..2179031c --- /dev/null +++ b/app/mdebug/swd.h @@ -0,0 +1,55 @@ +/* swd.h + * + * Copyright 2011 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SWDP_H_ +#define _SWDP_H_ + +void swd_init(void); +void swd_reset(void); +int swd_write(unsigned reg, unsigned val); +int swd_read(unsigned reg, unsigned *val); + +unsigned swd_set_clock(unsigned khz); +unsigned swo_set_clock(unsigned khz); +void swd_hw_reset(int assert); + +// swdp_read/write() register codes + +// Park Stop Parity Addr3 Addr2 RnW APnDP Start + +#define RD_IDCODE 0b10100101 +#define RD_DPCTRL 0b10001101 +#define RD_RESEND 0b10010101 +#define RD_BUFFER 0b10111101 + +#define WR_ABORT 0b10000001 +#define WR_DPCTRL 0b10101001 +#define WR_SELECT 0b10110001 +#define WR_BUFFER 0b10011001 + +#define RD_AP0 0b10000111 +#define RD_AP1 0b10101111 +#define RD_AP2 0b10110111 +#define RD_AP3 0b10011111 + +#define WR_AP0 0b10100011 +#define WR_AP1 0b10001011 +#define WR_AP2 0b10010011 +#define WR_AP3 0b10111011 + +#endif + diff --git a/app/mdebug/swo-uart1.c b/app/mdebug/swo-uart1.c new file mode 100644 index 00000000..7803a1dc --- /dev/null +++ b/app/mdebug/swo-uart1.c @@ -0,0 +1,151 @@ +/* swo-uart1.c + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> +#include <debug.h> +#include <reg.h> +#include <kernel/thread.h> + +#include <dev/udc.h> +#include <arch/arm/cm.h> + +#include <platform/lpc43xx-uart.h> +#include <platform/lpc43xx-gpdma.h> +#include <platform/lpc43xx-clocks.h> + +#include "rswdp.h" + +#define UART_BASE UART1_BASE +#define BASE_UART_CLK BASE_UART1_CLK + +extern uint8_t __lpc43xx_main_clock_sel; +extern uint32_t __lpc43xx_main_clock_mhz; + +#define TXNSIZE 128 +#define TXNCOUNT 4 + +typedef struct swo_txn { + unsigned buf[TXNSIZE/4 + 2]; + struct swo_txn *next; + udc_request_t *req; + unsigned busy; + unsigned num; +} txn_t; + +static txn_t TXN[TXNCOUNT]; +static txn_t *txwr = TXN; +static udc_endpoint_t *txept; + +void swo_start_dma(void *ptr); + +static void tx_done(udc_request_t *req, unsigned actual, int status) { + txn_t *txn = req->context; + txn->busy = 0; + if (txwr == txn) { + // writer wants to write here, and is waiting... + swo_start_dma(txn->buf + 2); + } +} + +void lpc43xx_DMA_IRQ(void) { + txn_t *txn = txwr; + arm_cm_irq_entry(); + writel(0xFF, DMA_INTTCCLR); + writel(0xFF, DMA_INTERRCLR); + if (udc_request_queue(txept, txn->req)) { + // failed, usb probably offline, just re-use the buffer + } else { + txn->busy = 1; + txwr = txn = txn->next; + } + if (!txn->busy) { + // if busy, when the usb txn completes, it will start dma then + swo_start_dma(txn->buf + 2); + } + arm_cm_irq_exit(0); +} + +void swo_init(udc_endpoint_t *_txept) { + int n; + txept = _txept; + for (n = 0; n < TXNCOUNT; n++) { + TXN[n].req = udc_request_alloc(); + TXN[n].req->context = TXN + n; + TXN[n].req->buffer = TXN[n].buf; + TXN[n].req->length = TXNSIZE + 8; + TXN[n].req->complete = tx_done; + TXN[n].num = n; + TXN[n].busy = 0; + TXN[n].next = TXN + (n + 1); + TXN[n].buf[0] = RSWD_TXN_ASYNC; + TXN[n].buf[1] = RSWD_MSG(CMD_SWO_DATA, 0, TXNSIZE); + } + TXN[n-1].next = TXN; + + // configure peripheral 4 as uart1_rx + writel((readl(DMAMUX_REG) & DMAMUX_M(4)) | DMAMUX_P(4, P4_UART1_RX), DMAMUX_REG); + writel(DMA_CONFIG_EN, DMA_CONFIG); + NVIC_EnableIRQ(DMA_IRQn); + + // kick off the process with an initial DMA + swo_start_dma(txwr->buf + 2); +} + +void swo_start_dma(void *ptr) { + writel(UART1_BASE + REG_RBR, DMA_SRC(0)); + writel((u32) ptr, DMA_DST(0)); + writel(0, DMA_LLI(0)); + writel(DMA_XFER_SIZE(TXNSIZE) | + DMA_SRC_BURST(BURST_1) | DMA_DST_BURST(BURST_4) | + DMA_SRC_BYTE | DMA_DST_WORD | DMA_SRC_MASTER1 | DMA_DST_MASTER0 | + DMA_DST_INCR | DMA_PROT1 | DMA_TC_IE, + DMA_CTL(0)); + writel(DMA_ENABLE | DMA_SRC_PERIPH(4) | DMA_FLOW_P2M_DMAc | DMA_TC_IRQ_EN, + DMA_CFG(0)); +} +void swo_config(unsigned mhz) { + if (mhz > 0) { + uint32_t div = __lpc43xx_main_clock_mhz / 16 / mhz; + writel(BASE_CLK_SEL(__lpc43xx_main_clock_sel), BASE_UART_CLK); + writel(LCR_DLAB, UART_BASE + REG_LCR); + writel(div & 0xFF, UART_BASE + REG_DLL); + writel((div >> 8) & 0xFF, UART_BASE + REG_DLM); + writel(LCR_WLS_8 | LCR_SBS_1, UART_BASE + REG_LCR); + writel(FCR_FIFOEN | FCR_RX_TRIG_1 | FCR_DMAMODE, UART_BASE + REG_FCR); + } +} + +unsigned swo_set_clock(unsigned khz) { + if (khz >= 12000) { + khz = 12000; + } else if (khz >= 8000) { + khz = 8000; + } else if (khz >= 6000) { + khz = 6000; + } else if (khz >= 4000) { + khz = 4000; + } else if (khz >= 3000) { + khz = 3000; + } else if (khz >= 2000) { + khz = 2000; + } else { + khz = 1000; + } + swo_config(khz * 1000); + return khz; +} + diff --git a/app/udctest/rules.mk b/app/udctest/rules.mk new file mode 100644 index 00000000..542785ae --- /dev/null +++ b/app/udctest/rules.mk @@ -0,0 +1,9 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/udctest.c + +include make/module.mk + diff --git a/app/udctest/udctest.c b/app/udctest/udctest.c new file mode 100644 index 00000000..4ccf997d --- /dev/null +++ b/app/udctest/udctest.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include <app.h> +#include <debug.h> +#include <string.h> +#include <stdlib.h> +#include <printf.h> +#include <dev/udc.h> + +udc_request_t *txreq; +udc_request_t *rxreq; +udc_endpoint_t *txept; +udc_endpoint_t *rxept; + +static char rxbuf[4096]; + +static void rx_complete(udc_request_t *req, unsigned actual, int status) { + //printf("rx done %d %d\n", actual, status); + if (status == 0) { + udc_request_queue(rxept, rxreq); + } +} + +static void tx_complete(udc_request_t *req, unsigned actual, int status) { + //printf("tx done %d %d\n", actual, status); + if (status == 0) { + udc_request_queue(txept, txreq); + } +} + +static void udctest_notify(udc_gadget_t *gadget, unsigned event) { + printf("event %d\n", event); + if (event == UDC_EVENT_ONLINE) { + udc_request_queue(rxept, rxreq); + udc_request_queue(txept, txreq); + } +} + +static udc_device_t udctest_device = { + .vendor_id = 0x18d1, + .product_id = 0xdb01, + .version_id = 0x0100, + .manufacturer = "Frobozz Magic USB Device Company", + .product = "Frobozzco USB Device", + .serialno = "00000005", +}; + +static udc_endpoint_t *udctest_endpoints[2]; + +static udc_gadget_t udctest_gadget = { + .notify = udctest_notify, + .ifc_class = 0xFF, + .ifc_subclass = 0x42, + .ifc_protocol = 0x01, + .ifc_endpoints = 2, + .ifc_string = "string", + .ept = udctest_endpoints, +}; + +static void udctest_init(const struct app_descriptor *app) +{ + printf("usbtest_init()\n"); + udc_init(&udctest_device); + udctest_endpoints[0] = txept = udc_endpoint_alloc(UDC_BULK_IN, 512); + udctest_endpoints[1] = rxept = udc_endpoint_alloc(UDC_BULK_OUT, 512); + txreq = udc_request_alloc(); + rxreq = udc_request_alloc(); + rxreq->buffer = rxbuf; + rxreq->length = sizeof(rxbuf); + rxreq->complete = rx_complete; + txreq->buffer = rxbuf; + txreq->length = sizeof(rxbuf); + txreq->complete = tx_complete; + udc_register_gadget(&udctest_gadget); +} + +static void udctest_entry(const struct app_descriptor *app, void *args) +{ + printf("udctest_entry()\n"); + udc_start(); +} + +APP_START(usbtest) + .init = udctest_init, + .entry = udctest_entry, +APP_END + + |