diff --git a/.gitignore b/.gitignore index b3b616ae..c9374098 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ clients/**/bin/** clients/**/obj/** electron-out hexcache +build *.user *.sw? diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 60d672b9..8f16e81e 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -306,3 +306,4 @@ * [Servo](/device/servo) * [Simulator](/device/simulator) * [Usb](/device/usb) + * [Flashing via HID (CMSIS-DAP)](/hidflash) diff --git a/docs/docs.md b/docs/docs.md index 9af87ce2..85cd57c4 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -29,4 +29,5 @@ * [Command Line Interface](/cli) * Learn about [packages](/packages) +* [Flashing via HID (CMSIS-DAP)](/hidflash) diff --git a/docs/hidflash.md b/docs/hidflash.md new file mode 100644 index 00000000..15f234bc --- /dev/null +++ b/docs/hidflash.md @@ -0,0 +1,63 @@ +# Flashing via HID (CMSIS-DAP) + +When the web app has access to a HID connection to the board, it can flash +the board via the hardware debugger interface. +The PXT localhost server can proxy HID connections (over a WebSocket), +and native apps can access HID via various custom APIs (which are +likely to have lower latency than the HID proxy). + +This is generally done via +writing a little flashing program to the RAM, then writing the page to be +flashed to the RAM, and then running the program. For next page, one keeps +the flashing program, but replaces the data. Internally, the DAPLink +software on the @boardname@ does the same. + +The flashing via DAP over HID is quite a bit slower than the regular +drag&drop kind. This is because of overheads of the DAP protocol +and the limited throughput of HID (1 packet, of maximum 64 bytes, per millisecond). +Additionally, the DAP protocol requires every HID packet to be acknowledged +effectively halving the bandwidth. +Thus, typical flashing speeds (using HID proxy) are around 14k/s, with a typical +full flash taking 15s. Theoretical maximum is around 25k/s. + +A custom flashing protocol, like [HF2](https://github.com/Microsoft/uf2/blob/master/hf2.md), +can achieve around 60k/s, however this would require updates of DAPLink software, +and is still not very fast. + +## Partial flashing + +Instead, we take care to only flash the pages that have changed. +In typical software development only a very small fragment of the program +changes on every re-deployment. Additionally, most of the program is pretty +much constant (two bootloaders, the softdevice, and the compiled C++ runtime). + +This is achieved by first deploying a small program which computes +checksums of every page. Then, these checksums are read from the device +and compared with checksums of pages of the `.hex` file to be deployed. +Only pages which checksums that do not match are flashed. + +The particular checksum algorithm used is [Murmur3](https://en.wikipedia.org/wiki/MurmurHash#MurmurHash3). +The algorithm is simplified by removing checks for unaligned data, or hashing +the data length, since all blocks hashed are of the same, aligned length. + +The Murmur3 hash was chosen since it's very fast (around 4x faster than CRC32 and around +15x faster than SHA256). Hashing the entire flash takes about 200ms. + +In fact, two 32 bit Murmur3 hashes (using different starting seeds) are computed in +parallel, to produce a 64 bit checksum. + +## Hash length analysis + +Let's compute the probability of some people running into trouble because +of hash collisions on pages. Assume: +* uniform distribution of hashes +* 10M users +* each user programming for 50h, and flashing every 2 minutes, i.e., 1500 flashes +* each flashing changing 10 pages + +With 64 bit hashes, the probability that a collision occurs +`1 - ((2^64 - 1) / 2^64) ^ (1e7 * 1500 * 10)` which is `0.000016` +(Bing says so; Google wrongly said `0`). +With 32 bit, even with only 1M users we get `97%` probability of some collisions. +The uniformity of hashes is questionable, but the `0.000016` +gives us some wiggle room. diff --git a/editor/dapjs.d.ts b/editor/dapjs.d.ts new file mode 100644 index 00000000..310acbc9 --- /dev/null +++ b/editor/dapjs.d.ts @@ -0,0 +1,421 @@ +declare namespace DapJS { + export interface IHID { + write(data: ArrayBuffer): Promise; + read(): Promise; + close(): Promise; + // sends each of commands and expects one packet in response + // this makes for better performance when HID access is proxied + sendMany?(commands: Uint8Array[]): Promise; + } + + export class DAP { + constructor(device: IHID); + reconnect(): Promise; + init(): Promise; + close(): Promise; + } + + /** + * # Memory Interface + * + * Controls access to the target's memory. + * + * ## Usage + * + * Using an instance of `CortexM`, as described before, we can simply read and + * write numbers to memory as follows: + * + * ```typescript + * const mem = core.memory; + * + * // NOTE: the address parameter must be word (4-byte) aligned. + * await mem.write32(0x200000, 12345); + * const val = await mem.read32(0x200000); + * + * // val === 12345 + * + * // NOTE: the address parameter must be half-word (2-byte) aligned + * await mem.write16(0x2000002, 65534); + * const val16 = await mem.read16(0x2000002); + * + * // val16 === 65534 + * ``` + * + * To write a larger block of memory, we can use `readBlock` and `writeBlock`. Again, + * these blocks must be written to word-aligned addresses in memory. + * + * ```typescript + * const data = new Uint32Array([0x1234, 0x5678, 0x9ABC, 0xDEF0]); + * await mem.writeBlock(0x200000, data); + * + * const readData = await mem.readBlock(0x200000, data.length, 0x100); + * ``` + * + * ## See also + * + * `PreparedMemoryCommand` provides an equivalent API with better performance (in some + * cases) by enabling batched memory operations. + */ + export class Memory { + private dev; + constructor(dev: DAP); + /** + * Write a 32-bit word to the specified (word-aligned) memory address. + * + * @param addr Memory address to write to + * @param data Data to write (values above 2**32 will be truncated) + */ + write32(addr: number, data: number): Promise; + /** + * Write a 16-bit word to the specified (half word-aligned) memory address. + * + * @param addr Memory address to write to + * @param data Data to write (values above 2**16 will be truncated) + */ + write16(addr: number, data: number): Promise; + /** + * Read a 32-bit word from the specified (word-aligned) memory address. + * + * @param addr Memory address to read from. + */ + read32(addr: number): Promise; + /** + * Read a 16-bit word from the specified (half word-aligned) memory address. + * + * @param addr Memory address to read from. + */ + read16(addr: number): Promise; + /** + * Reads a block of memory from the specified memory address. + * + * @param addr Address to read from + * @param words Number of words to read + * @param pageSize Memory page size + */ + readBlock(addr: number, words: number, pageSize: number): Promise; + /** + * Write a block of memory to the specified memory address. + * + * @param addr Memory address to write to. + * @param words Array of 32-bit words to write to memory. + */ + writeBlock(addr: number, words: Uint32Array): Promise; + private readBlockCore(addr, words); + private writeBlockCore(addr, words); + } + + /** + * # Cortex M + * + * Manages access to a CPU core, and its associated memory and debug functionality. + * + * > **NOTE:** all of the methods that involve interaction with the CPU core + * > are asynchronous, so must be `await`ed, or explicitly handled as a Promise. + * + * ## Usage + * + * First, let's create an instance of `CortexM`, using an associated _Debug Access + * Port_ (DAP) instance that we created earlier. + * + * ```typescript + * const core = new CortexM(dap); + * ``` + * + * Now, we can halt and resume the core just like this: + * + * > **NOTE:** If you're not using ES2017, you can replace the use of `async` and + * > `await` with direct use of Promises. These examples also need to be run within + * > an `async` function for `async` to be used. + * + * ```typescript + * await core.halt(); + * await core.resume(); + * ``` + * + * Resetting the core is just as easy: + * + * ```typescript + * await core.reset(); + * ``` + * + * You can even halt immediately after reset: + * + * ```typescript + * await core.reset(true); + * ``` + * + * We can also read and write 32-bit values to/from core registers: + * + * ```typescript + * const sp = await core.readCoreRegister(CortexReg.SP); + * + * await core.writeCoreRegister(CortexReg.R0, 0x1000); + * await core.writeCoreRegister(CortexReg.PC, 0x1234); + * ``` + * + * ### See also + * + * For details on debugging and memory features, see the documentation for + * `Debug` and `Memory`. + */ + export class CortexM { + /** + * Read and write to on-chip memory associated with this CPU core. + */ + memory: Memory; + /** + * Control the CPU's debugging features. + */ + debug: Debug; + /** + * Underlying Debug Access Port (DAP). + */ + private dev; + constructor(device: DAP); + /** + * Initialise the debug access port on the device, and read the device type. + */ + init(): Promise; + /** + * Read the current state of the CPU. + * + * @returns A member of the `CoreState` enum corresponding to the current status of the CPU. + */ + getState(): Promise; + + /** + * Read a core register from the CPU (e.g. r0...r15, pc, sp, lr, s0...) + * + * @param no Member of the `CortexReg` enum - an ARM Cortex CPU general-purpose register. + */ + readCoreRegister(no: CortexReg): Promise; + /** + * Write a 32-bit word to the specified CPU general-purpose register. + * + * @param no Member of the `CortexReg` enum - an ARM Cortex CPU general-purpose register. + * @param val Value to be written. + */ + writeCoreRegister(no: CortexReg, val: number): Promise; + /** + * Halt the CPU core. + */ + halt(): Promise; + /** + * Resume the CPU core. + */ + resume(): Promise; + /** + * Find out whether the CPU is halted. + */ + isHalted(): Promise; + /** + * Read the current status of the CPU. + * + * @returns Object containing the contents of the `DHCSR` register, the `DFSR` register, and a boolean value + * stating the current halted state of the CPU. + */ + status(): Promise<{ + dfsr: number; + dhscr: number; + isHalted: boolean; + }>; + /** + * Reset the CPU core. This currently does a software reset - it is also technically possible to perform a 'hard' + * reset using the reset pin from the debugger. + */ + reset(halt?: boolean): Promise; + /** + * Run specified machine code natively on the device. Assumes usual C calling conventions + * - returns the value of r0 once the program has terminated. The program _must_ terminate + * in order for this function to return. This can be achieved by placing a `bkpt` + * instruction at the end of the function. + * + * @param code array containing the machine code (32-bit words). + * @param address memory address at which to place the code. + * @param pc initial value of the program counter. + * @param lr initial value of the link register. + * @param sp initial value of the stack pointer. + * @param upload should we upload the code before running it. + * @param args set registers r0...rn before running code + * + * @returns A promise for the value of r0 on completion of the function call. + */ + runCode(code: Uint32Array, address: number, pc: number, lr: number, sp: number, upload: boolean, ...args: number[]): Promise; + /** + * Spin until the chip has halted. + */ + waitForHalt(timeout?: number): Promise; + + prepareCommand(): PreparedCortexMCommand; + + private softwareReset(); + } + + /** + * # Cortex M: Prepared Command + * + * Allows batching of Cortex M-related commands, such as writing to a register, + * halting and resuming the core. + * + * ## Example + * + * When preparing the sequence of commands, we can use the same API to prepare + * a command as we would to execute them immediately. + * + * ```typescript + * // Note that only the .go method is asynchronous. + * + * const prep = core.prepareCommand(); + * prep.writeCoreRegister(CortexReg.R0, 0x1000); + * prep.writeCoreRegister(CortexReg.R1, 0x0); + * prep.writeCoreRegister(CortexReg.PC, 0x2000000); + * prep.resume(); + * ``` + * + * We can then execute them as efficiently as possible by combining them together + * and executing them like so. + * + * ```typescript + * await prep.go(); + * ``` + * + * The code above is equivalent to the following _non-prepared_ command: + * + * ```typescript + * await core.writeCoreRegister(CortexReg.R0, 0x1000); + * await core.writeCoreRegister(CortexReg.R1, 0x0); + * await core.writeCoreRegister(CortexReg.PC, 0x2000000); + * await core.resume(); + * ``` + * + * Since the batched version of this code avoids making three round-trips to the + * target, we are able to significantly improve performance. This is especially + * noticable when uploading a binary to flash memory, where are large number of + * repetetive commands are being used. + * + * ## Explanation + * + * For a detailed explanation of why prepared commands are used in DAP.js, see the + * documentation for `PreparedDapCommand`. + */ + export class PreparedCortexMCommand { + private cmd; + constructor(dap: DAP); + /** + * Schedule a 32-bit integer to be written to a core register. + * + * @param no Core register to be written. + * @param val Value to write. + */ + writeCoreRegister(no: CortexReg, val: number): void; + /** + * Schedule a halt command to be written to the CPU. + */ + halt(): void; + /** + * Schedule a resume command to be written to the CPU. + */ + resume(): void; + /** + * Execute all scheduled commands. + */ + go(): Promise; + } + + + export const enum CortexReg { + R0 = 0, + R1 = 1, + R2 = 2, + R3 = 3, + R4 = 4, + R5 = 5, + R6 = 6, + R7 = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + SP = 13, + LR = 14, + PC = 15, + XPSR = 16, + MSP = 17, + PSP = 18, + PRIMASK = 20, + CONTROL = 20, + } + export const enum CoreState { + TARGET_RESET = 0, + TARGET_LOCKUP = 1, + TARGET_SLEEPING = 2, + TARGET_HALTED = 3, + TARGET_RUNNING = 4, + } + + + + /** + * # Debug Interface + * + * Keeps track of breakpoints set on the target, as well as deciding whether to + * use a hardware breakpoint or a software breakpoint. + * + * ## Usage + * + * ```typescript + * const dbg = core.debug; + * + * await dbg.setBreakpoint(0x123456); + * + * // resume the core and wait for the breakpoint + * await core.resume(); + * await core.waitForHalt(); + * + * // step forward one instruction + * await dbg.step(); + * + * // remove the breakpoint + * await dbg.deleteBreakpoint(0x123456); + * ``` + */ + export class Debug { + private core; + private breakpoints; + private availableHWBreakpoints; + private totalHWBreakpoints; + private enabled; + constructor(core: CortexM); + init(): Promise; + /** + * Enable debugging on the target CPU + */ + enable(): Promise; + /** + * Set breakpoints at specified memory addresses. + * + * @param addrs An array of memory addresses at which to set breakpoints. + */ + setBreakpoint(addr: number): Promise; + deleteBreakpoint(addr: number): Promise; + /** + * Step the processor forward by one instruction. + */ + step(): Promise; + /** + * Set up (and disable) the Flash Patch & Breakpoint unit. It will be enabled when + * the first breakpoint is set. + * + * Also reads the number of available hardware breakpoints. + */ + private setupFpb(); + /** + * Enable or disable the Flash Patch and Breakpoint unit (FPB). + * + * @param enabled + */ + private setFpbEnabled(enabled?); + } + +} \ No newline at end of file diff --git a/editor/extension.ts b/editor/extension.ts index 8656e7d4..c18dfcdb 100644 --- a/editor/extension.ts +++ b/editor/extension.ts @@ -1,8 +1,366 @@ /// +interface Math { + imul(x: number, y: number): number; +} namespace pxt.editor { - initExtensionsAsync = function(opts: pxt.editor.ExtensionOptions): Promise { + import UF2 = pxtc.UF2; + + const pageSize = 1024; + const numPages = 256; + + function murmur3_core(data: Uint8Array) { + let h0 = 0x2F9BE6CC; + let h1 = 0x1EC3A6C8; + + for (let i = 0; i < data.length; i += 4) { + let k = HF2.read32(data, i) >>> 0 + k = Math.imul(k, 0xcc9e2d51); + k = (k << 15) | (k >>> 17); + k = Math.imul(k, 0x1b873593); + + h0 ^= k; + h1 ^= k; + h0 = (h0 << 13) | (h0 >>> 19); + h1 = (h1 << 13) | (h1 >>> 19); + h0 = (Math.imul(h0, 5) + 0xe6546b64) >>> 0; + h1 = (Math.imul(h1, 5) + 0xe6546b64) >>> 0; + } + return [h0, h1] + } + + class DAPWrapper { + cortexM: DapJS.CortexM + + constructor(h: HF2.PacketIO) { + let pbuf = new U.PromiseBuffer() + + let sendMany = (cmds: Uint8Array[]) => { + return h.talksAsync(cmds.map(c => ({ cmd: 0, data: c }))) + } + + if (!h.talksAsync) + sendMany = null + + let dev = new DapJS.DAP({ + write: writeAsync, + close: closeAsync, + read: readAsync, + sendMany: sendMany + }) + this.cortexM = new DapJS.CortexM(dev) + + h.onData = buf => { + pbuf.push(buf) + } + + function writeAsync(data: ArrayBuffer) { + h.sendPacketAsync(new Uint8Array(data)) + return Promise.resolve() + } + + function readAsync() { + return pbuf.shiftAsync() + } + + function closeAsync() { + return h.disconnectAsync() + } + } + + reconnectAsync(first: boolean) { + return this.cortexM.init() + } + } + + function dapAsync() { + return pxt.HF2.mkPacketIOAsync() + .then(h => { + let w = new DAPWrapper(h) + return w.reconnectAsync(true) + .then(() => w) + }) + } + + let noHID = false + + let initPromise: Promise + function initAsync() { + if (initPromise) + return initPromise + + let canHID = false + if (U.isNodeJS) { + canHID = true + } else { + const forceHexDownload = /forceHexDownload/i.test(window.location.href); + if (Cloud.isLocalHost() && Cloud.localToken && !forceHexDownload) + canHID = true + } + + if (noHID) + canHID = false + + if (canHID) { + initPromise = dapAsync() + .catch(err => { + initPromise = null + noHID = true + return Promise.reject(err) + }) + } else { + noHID = true + initPromise = Promise.reject(new Error("no HID")) + } + + return initPromise + } + + function pageAlignBlocks(blocks: UF2.Block[], pageSize: number) { + U.assert(pageSize % 256 == 0) + let res: UF2.Block[] = [] + for (let i = 0; i < blocks.length;) { + let b0 = blocks[i] + let newbuf = new Uint8Array(pageSize) + let startPad = b0.targetAddr & (pageSize - 1) + let newAddr = b0.targetAddr - startPad + for (; i < blocks.length; ++i) { + let b = blocks[i] + if (b.targetAddr + b.payloadSize > newAddr + pageSize) + break + U.memcpy(newbuf, b.targetAddr - newAddr, b.data, 0, b.payloadSize) + } + let bb = U.flatClone(b0) + bb.data = newbuf + bb.targetAddr = newAddr + bb.payloadSize = pageSize + res.push(bb) + } + return res + } + + const flashPageBINquick = new Uint32Array([ + 0xbe00be00, // bkpt - LR is set to this + 0x2480b5f0, 0x00e42300, 0x58cd58c2, 0xd10342aa, 0x42a33304, 0xbdf0d1f8, + 0x4b162502, 0x509d4a16, 0x2d00591d, 0x24a1d0fc, 0x511800e4, 0x3cff3c09, + 0x591e0025, 0xd0fc2e00, 0x509c2400, 0x2c00595c, 0x2401d0fc, 0x509c2580, + 0x595c00ed, 0xd0fc2c00, 0x00ed2580, 0x002e2400, 0x5107590f, 0x2f00595f, + 0x3404d0fc, 0xd1f742ac, 0x50992100, 0x2a00599a, 0xe7d0d0fc, 0x4001e000, + 0x00000504, + ]) + + // doesn't check if data is already there - for timing + const flashPageBIN = new Uint32Array([ + 0xbe00be00, // bkpt - LR is set to this + 0x2402b5f0, 0x4a174b16, 0x2480509c, 0x002500e4, 0x2e00591e, 0x24a1d0fc, + 0x511800e4, 0x2c00595c, 0x2400d0fc, 0x2480509c, 0x002500e4, 0x2e00591e, + 0x2401d0fc, 0x595c509c, 0xd0fc2c00, 0x00ed2580, 0x002e2400, 0x5107590f, + 0x2f00595f, 0x3404d0fc, 0xd1f742ac, 0x50992100, 0x2a00599a, 0xbdf0d0fc, + 0x4001e000, 0x00000504, + ]) + + // void computeHashes(uint32_t *dst, uint8_t *ptr, uint32_t pageSize, uint32_t numPages) + const computeChecksums2 = new Uint32Array([ + 0x4c27b5f0, 0x44a52680, 0x22009201, 0x91004f25, 0x00769303, 0x24080013, + 0x25010019, 0x40eb4029, 0xd0002900, 0x3c01407b, 0xd1f52c00, 0x468c0091, + 0xa9044665, 0x506b3201, 0xd1eb42b2, 0x089b9b01, 0x23139302, 0x9b03469c, + 0xd104429c, 0x2000be2a, 0x449d4b15, 0x9f00bdf0, 0x4d149e02, 0x49154a14, + 0x3e01cf08, 0x2111434b, 0x491341cb, 0x405a434b, 0x4663405d, 0x230541da, + 0x4b10435a, 0x466318d2, 0x230541dd, 0x4b0d435d, 0x2e0018ed, 0x6002d1e7, + 0x9a009b01, 0x18d36045, 0x93003008, 0xe7d23401, 0xfffffbec, 0xedb88320, + 0x00000414, 0x1ec3a6c8, 0x2f9be6cc, 0xcc9e2d51, 0x1b873593, 0xe6546b64, + ]) + + let startTime = 0 + function log(msg: string) { + let now = Date.now() + if (!startTime) startTime = now + now -= startTime + let ts = ("00000" + now).slice(-5) + pxt.log(`HID ${ts}: ${msg}`) + } + + const membase = 0x20000000 + const loadAddr = membase + const dataAddr = 0x20002000 + const stackAddr = 0x20001000 + + export const bufferConcat = (bufs: Uint8Array[]) => { + let len = 0; + for (const b of bufs) { + len += b.length; + } + const r = new Uint8Array(len); + len = 0; + for (const b of bufs) { + r.set(b, len); + len += b.length; + } + return r; + }; + + + function getFlashChecksumsAsync(wrap: DAPWrapper) { + log("getting existing flash checksums") + let pages = numPages + return wrap.cortexM.runCode(computeChecksums2, loadAddr, loadAddr + 1, 0xffffffff, stackAddr, true, + dataAddr, 0, pageSize, pages) + .then(() => wrap.cortexM.memory.readBlock(dataAddr, pages * 2, pageSize)) + } + + function onlyChanged(blocks: UF2.Block[], checksums: Uint8Array) { + return blocks.filter(b => { + let idx = b.targetAddr / pageSize + U.assert((idx | 0) == idx) + U.assert(b.data.length == pageSize) + if (idx * 8 + 8 > checksums.length) + return true // out of range? + let c0 = HF2.read32(checksums, idx * 8) + let c1 = HF2.read32(checksums, idx * 8 + 4) + let ch = murmur3_core(b.data) + if (c0 == ch[0] && c1 == ch[1]) + return false + return true + }) + } + + export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false): Promise { + let saveHexAsync = () => { + if (isCli) { + return Promise.resolve() + } else { + return pxt.commands.saveOnlyAsync(resp) + } + } + + startTime = 0 + + if (noHID) return saveHexAsync() + + let wrap: DAPWrapper + + log("init") + + let logV = (msg: string) => { } + //let logV = log + + const runFlash = (b: UF2.Block, dataAddr: number) => { + const cmd = wrap.cortexM.prepareCommand(); + + cmd.halt(); + + cmd.writeCoreRegister(DapJS.CortexReg.PC, loadAddr + 4 + 1); + cmd.writeCoreRegister(DapJS.CortexReg.LR, loadAddr + 1); + cmd.writeCoreRegister(DapJS.CortexReg.SP, stackAddr); + + cmd.writeCoreRegister(0, b.targetAddr); + cmd.writeCoreRegister(1, dataAddr); + + return Promise.resolve() + .then(() => { + logV("setregs") + return cmd.go() + }) + .then(() => { + logV("dbg en") + // starts the program + return wrap.cortexM.debug.enable() + }) + } + + let checksums: Uint8Array + + return initAsync() + .then(w => { + wrap = w + log("reset") + return wrap.cortexM.reset(true) + }) + .then(() => getFlashChecksumsAsync(wrap)) + .then(buf => { + checksums = buf + log("write code") + return wrap.cortexM.memory.writeBlock(loadAddr, flashPageBIN) + }) + .then(() => { + log("convert") + // TODO this is seriously inefficient (130ms on a fast machine) + let uf2 = UF2.newBlockFile() + UF2.writeHex(uf2, resp.outfiles[pxtc.BINARY_HEX].split(/\r?\n/)) + let bytes = U.stringToUint8Array(UF2.serializeFile(uf2)) + let parsed = UF2.parseFile(bytes) + + let aligned = pageAlignBlocks(parsed, pageSize) + log(`initial: ${aligned.length} pages`) + aligned = onlyChanged(aligned, checksums) + log(`incremental: ${aligned.length} pages`) + + return Promise.mapSeries(U.range(aligned.length), + i => { + let b = aligned[i] + if (b.targetAddr >= 0x10000000) + return Promise.resolve() + + logV("about to write at 0x" + b.targetAddr.toString(16)) + + let writeBl = Promise.resolve() + + let thisAddr = (i & 1) ? dataAddr : dataAddr + pageSize + let nextAddr = (i & 1) ? dataAddr + pageSize : dataAddr + + if (i == 0) { + let u32data = new Uint32Array(b.data.length / 4) + for (let i = 0; i < b.data.length; i += 4) + u32data[i >> 2] = HF2.read32(b.data, i) + writeBl = wrap.cortexM.memory.writeBlock(thisAddr, u32data) + } + + return writeBl + .then(() => runFlash(b, thisAddr)) + .then(() => { + let next = aligned[i + 1] + if (!next) + return Promise.resolve() + logV("write next") + let buf = new Uint32Array(next.data.buffer) + return wrap.cortexM.memory.writeBlock(nextAddr, buf) + }) + .then(() => { + logV("wait") + return wrap.cortexM.waitForHalt(500) + }) + .then(() => { + logV("done block") + }) + }) + .then(() => { + log("flash done") + return wrap.cortexM.reset(false) + }) + }) + .catch(e => { + // if we failed to initalize, retry + if (noHID) + return saveHexAsync() + else + return Promise.reject(e) + }) + } + + initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise { pxt.debug('loading microbit target extensions...') + + if (!Math.imul) + Math.imul = (a, b) => { + var ah = (a >>> 16) & 0xffff; + var al = a & 0xffff; + var bh = (b >>> 16) & 0xffff; + var bl = b & 0xffff; + // the shift by 0 fixes the sign on the high part + // the final |0 converts the unsigned value into a signed value + return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0); + }; + const res: pxt.editor.ExtensionResult = { hexFileImporters: [{ id: "blockly", @@ -13,17 +371,19 @@ namespace pxt.editor { }, name: data.meta.name }) }, { - id: "td", - canImport: data => data.meta.cloudId == "microbit.co.uk" && data.meta.editor == "touchdevelop", - importAsync: (project, data) => - project.createProjectAsync({ - filesOverride: { "main.blocks": "", "main.ts": " " }, - name: data.meta.name - }) - .then(() => project.convertTouchDevelopToTypeScriptAsync(data.source)) - .then(text => project.overrideTypescriptFile(text)) - }] + id: "td", + canImport: data => data.meta.cloudId == "microbit.co.uk" && data.meta.editor == "touchdevelop", + importAsync: (project, data) => + project.createProjectAsync({ + filesOverride: { "main.blocks": "", "main.ts": " " }, + name: data.meta.name + }) + .then(() => project.convertTouchDevelopToTypeScriptAsync(data.source)) + .then(text => project.overrideTypescriptFile(text)) + }] }; + pxt.commands.deployCoreAsync = deployCoreAsync; return Promise.resolve(res); } -} \ No newline at end of file + +} diff --git a/editor/tsconfig.json b/editor/tsconfig.json index de974d4b..9b2e4c02 100644 --- a/editor/tsconfig.json +++ b/editor/tsconfig.json @@ -8,5 +8,6 @@ "rootDir": ".", "newLine": "LF", "sourceMap": false - } -} \ No newline at end of file + }, + "prepend": ["../external/dapjs.js"] +} diff --git a/external/dapjs.js b/external/dapjs.js new file mode 100644 index 00000000..4afceba1 --- /dev/null +++ b/external/dapjs.js @@ -0,0 +1,3038 @@ +var DapJS = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 4); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var _this = this; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.readUInt32LE = function (b, idx) { + return (b[idx] | + (b[idx + 1] << 8) | + (b[idx + 2] << 16) | + (b[idx + 3] << 24)) >>> 0; +}; +exports.bufferConcat = function (bufs) { + var len = 0; + for (var _i = 0, bufs_1 = bufs; _i < bufs_1.length; _i++) { + var b = bufs_1[_i]; + len += b.length; + } + var r = new Uint8Array(len); + len = 0; + for (var _a = 0, bufs_2 = bufs; _a < bufs_2.length; _a++) { + var b = bufs_2[_a]; + r.set(b, len); + len += b.length; + } + return r; +}; +exports.delay = function (t) { return __awaiter(_this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, new Promise(function (resolve) { + setTimeout(resolve, t); + })]; + }); +}); }; +exports.addInt32 = function (arr, val) { + if (!arr) { + arr = []; + } + arr.push(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff); + return arr; +}; +exports.hex = function (v) { + return "0x" + v.toString(16); +}; +exports.rid = function (v) { + var m = [ + "DP_0x0", + "DP_0x4", + "DP_0x8", + "DP_0xC", + "AP_0x0", + "AP_0x4", + "AP_0x8", + "AP_0xC", + ]; + return m[v] || "?"; +}; +exports.bank = function (addr) { + var APBANKSEL = 0x000000f0; + return (addr & APBANKSEL) | (addr & 0xff000000); +}; +exports.apReg = function (r, mode) { + var v = r | mode | 1 /* AP_ACC */; + return (4 + ((v & 0x0c) >> 2)); +}; +exports.bufToUint32Array = function (buf) { + exports.assert((buf.length & 3) === 0); + var r = []; + if (!buf.length) { + return r; + } + r[buf.length / 4 - 1] = 0; + for (var i = 0; i < r.length; ++i) { + r[i] = exports.readUInt32LE(buf, i << 2); + } + return r; +}; +exports.assert = function (cond) { + if (!cond) { + throw new Error("assertion failed"); + } +}; +exports.regRequest = function (regId, isWrite) { + if (isWrite === void 0) { isWrite = false; } + var request = !isWrite ? 2 /* READ */ : 0 /* WRITE */; + if (regId < 4) { + request |= 0 /* DP_ACC */; + } + else { + request |= 1 /* AP_ACC */; + } + request |= (regId & 3) << 2; + return request; +}; +exports.hexBytes = function (bytes) { + var chk = 0; + var r = ":"; + bytes.forEach(function (b) { return chk += b; }); + bytes.push((-chk) & 0xff); + bytes.forEach(function (b) { return r += ("0" + b.toString(16)).slice(-2); }); + return r.toUpperCase(); +}; +exports.hex2bin = function (hexstr) { + var array = new Uint8Array(hexstr.length / 2); + for (var i = 0; i < hexstr.length / 2; i++) { + array[i] = parseInt(hexstr.substr(2 * i, 2), 16); + } + return array; +}; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * # Prepared Memory Command + * + * Allows multiple memory operations to be batched together to improve HID + * interface utilisation. + * + * ## Usage + * + * Similarly to `CortexMPreparedCommand` and `DapPreparedCommand`, a convenience + * function exists to quickly create a prepared memory command: + * + * ```typescript + * const prep = core.memory.prepareCommand(); + * ``` + * + * You can then construct the sequence of commands using the same API as `Memory`. + * + * ```typescript + * prep.write32(0x20000, 1234); + * prep.write32(0x12344, 5678); + * prep.write16(0x12346, 123); + * ``` + * + * And then dispatch the prepared commands asynchronously: + * + * ```typescript + * await prep.go(); + * ``` + */ +var PreparedMemoryCommand = (function () { + function PreparedMemoryCommand(dap) { + this.cmd = dap.prepareCommand(); + } + /** + * Schedule a 32-bit memory write operation. + * + * @param addr Word-aligned memory address to write to. + * @param data Number to be written. + */ + PreparedMemoryCommand.prototype.write32 = function (addr, data) { + this.cmd.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 2 /* CSW_SIZE32 */); + this.cmd.writeAp(4 /* TAR */, addr); + this.cmd.writeAp(12 /* DRW */, data); + }; + /** + * Schedule a 16-bit memory write operation. + * + * @param addr Half word-aligned memory address to write to. + * @param data Number to be written. + */ + PreparedMemoryCommand.prototype.write16 = function (addr, data) { + data = data << ((addr & 0x02) << 3); + this.cmd.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 1 /* CSW_SIZE16 */); + this.cmd.writeAp(4 /* TAR */, addr); + this.cmd.writeAp(12 /* DRW */, data); + }; + /** + * Schedule a 32-bit memory read operation. + * + * @param addr Word-aligned memory address to read from. + */ + PreparedMemoryCommand.prototype.read32 = function (addr) { + this.cmd.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 2 /* CSW_SIZE32 */); + this.cmd.writeAp(4 /* TAR */, addr); + this.cmd.readAp(12 /* DRW */); + }; + /** + * Schedule a 16-bit memory read operation. + * + * FIXME: the values need to be shifted after being read. + * + * @param addr Half word-aligned memory address to read from. + */ + PreparedMemoryCommand.prototype.read16 = function (addr) { + this.cmd.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 1 /* CSW_SIZE16 */); + this.cmd.writeAp(4 /* TAR */, addr); + this.cmd.readAp(12 /* DRW */); + }; + /** + * Execute all commands asynchronously. + */ + PreparedMemoryCommand.prototype.go = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.cmd.go()]; + }); + }); + }; + return PreparedMemoryCommand; +}()); +exports.PreparedMemoryCommand = PreparedMemoryCommand; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var debug_1 = __webpack_require__(5); +var memory_1 = __webpack_require__(7); +var prepared_1 = __webpack_require__(1); +var util_1 = __webpack_require__(0); +var constants_1 = __webpack_require__(3); +var prepared_2 = __webpack_require__(8); +/** + * # Cortex M + * + * Manages access to a CPU core, and its associated memory and debug functionality. + * + * > **NOTE:** all of the methods that involve interaction with the CPU core + * > are asynchronous, so must be `await`ed, or explicitly handled as a Promise. + * + * ## Usage + * + * First, let's create an instance of `CortexM`, using an associated _Debug Access + * Port_ (DAP) instance that we created earlier. + * + * ```typescript + * const core = new CortexM(dap); + * ``` + * + * Now, we can halt and resume the core just like this: + * + * > **NOTE:** If you're not using ES2017, you can replace the use of `async` and + * > `await` with direct use of Promises. These examples also need to be run within + * > an `async` function for `async` to be used. + * + * ```typescript + * await core.halt(); + * await core.resume(); + * ``` + * + * Resetting the core is just as easy: + * + * ```typescript + * await core.reset(); + * ``` + * + * You can even halt immediately after reset: + * + * ```typescript + * await core.reset(true); + * ``` + * + * We can also read and write 32-bit values to/from core registers: + * + * ```typescript + * const sp = await core.readCoreRegister(CortexReg.SP); + * + * await core.writeCoreRegister(CortexReg.R0, 0x1000); + * await core.writeCoreRegister(CortexReg.PC, 0x1234); + * ``` + * + * ### See also + * + * For details on debugging and memory features, see the documentation for + * `Debug` and `Memory`. + */ +var CortexM = (function () { + function CortexM(device) { + this.dev = device; + this.memory = new memory_1.Memory(device); + this.debug = new debug_1.Debug(this); + } + /** + * Initialise the debug access port on the device, and read the device type. + */ + CortexM.prototype.init = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.dev.init()]; + case 1: + _a.sent(); + // FIXME: don't run this if security is enabled on the K64F + return [4 /*yield*/, this.debug.init()]; + case 2: + // FIXME: don't run this if security is enabled on the K64F + _a.sent(); + return [4 /*yield*/, this.readCoreType()]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Read the current state of the CPU. + * + * @returns A member of the `CoreState` enum corresponding to the current status of the CPU. + */ + CortexM.prototype.getState = function () { + return __awaiter(this, void 0, void 0, function () { + var dhcsr, newDHCSR; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.memory.read32(3758157296 /* DHCSR */)]; + case 1: + dhcsr = _a.sent(); + if (!(dhcsr & 33554432 /* S_RESET_ST */)) return [3 /*break*/, 3]; + return [4 /*yield*/, this.memory.read32(3758157296 /* DHCSR */)]; + case 2: + newDHCSR = _a.sent(); + if (newDHCSR & 33554432 /* S_RESET_ST */ && !(newDHCSR & 16777216 /* S_RETIRE_ST */)) { + return [2 /*return*/, 0 /* TARGET_RESET */]; + } + _a.label = 3; + case 3: + if (dhcsr & 524288 /* S_LOCKUP */) { + return [2 /*return*/, 1 /* TARGET_LOCKUP */]; + } + else if (dhcsr & 262144 /* S_SLEEP */) { + return [2 /*return*/, 2 /* TARGET_SLEEPING */]; + } + else if (dhcsr & 131072 /* S_HALT */) { + return [2 /*return*/, 3 /* TARGET_HALTED */]; + } + else { + return [2 /*return*/, 4 /* TARGET_RUNNING */]; + } + return [2 /*return*/]; + } + }); + }); + }; + /** + * Read the CPUID register from the CPU, and interpret its meaning in terms of implementer, + * architecture and core type. + */ + CortexM.prototype.readCoreType = function () { + return __awaiter(this, void 0, void 0, function () { + var cpuid, implementer, arch, coreType; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.memory.read32(3758157056 /* CPUID */)]; + case 1: + cpuid = _a.sent(); + implementer = ((cpuid & constants_1.CPUID_IMPLEMENTER_MASK) >> constants_1.CPUID_IMPLEMENTER_POS); + arch = ((cpuid & constants_1.CPUID_ARCHITECTURE_MASK) >> constants_1.CPUID_ARCHITECTURE_POS); + coreType = ((cpuid & constants_1.CPUID_PARTNO_MASK) >> constants_1.CPUID_PARTNO_POS); + console.debug("Found an ARM " + constants_1.CoreNames.get(coreType)); + return [2 /*return*/, [implementer, arch, coreType]]; + } + }); + }); + }; + CortexM.prototype.prepareCommand = function () { + return new prepared_2.PreparedCortexMCommand(this.dev); + }; + /** + * Read a core register from the CPU (e.g. r0...r15, pc, sp, lr, s0...) + * + * @param no Member of the `CortexReg` enum - an ARM Cortex CPU general-purpose register. + */ + CortexM.prototype.readCoreRegister = function (no) { + return __awaiter(this, void 0, void 0, function () { + var v; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.memory.write32(3758157300 /* DCRSR */, no)]; + case 1: + _a.sent(); + return [4 /*yield*/, this.memory.read32(3758157296 /* DHCSR */)]; + case 2: + v = _a.sent(); + util_1.assert(v & 65536 /* S_REGRDY */); + return [4 /*yield*/, this.memory.read32(3758157304 /* DCRDR */)]; + case 3: return [2 /*return*/, _a.sent()]; + } + }); + }); + }; + /** + * Write a 32-bit word to the specified CPU general-purpose register. + * + * @param no Member of the `CortexReg` enum - an ARM Cortex CPU general-purpose register. + * @param val Value to be written. + */ + CortexM.prototype.writeCoreRegister = function (no, val) { + return __awaiter(this, void 0, void 0, function () { + var prep, v; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = new prepared_1.PreparedMemoryCommand(this.dev); + prep.write32(3758157304 /* DCRDR */, val); + prep.write32(3758157300 /* DCRSR */, no | 65536 /* DCRSR_REGWnR */); + prep.read32(3758157296 /* DHCSR */); + return [4 /*yield*/, prep.go()]; + case 1: + v = (_a.sent())[0]; + util_1.assert(v & 65536 /* S_REGRDY */); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Halt the CPU core. + */ + CortexM.prototype.halt = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.memory.write32(3758157296 /* DHCSR */, -1604386816 /* DBGKEY */ | 1 /* C_DEBUGEN */ | 2 /* C_HALT */)]; + }); + }); + }; + /** + * Resume the CPU core. + */ + CortexM.prototype.resume = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.isHalted()]; + case 1: + if (!_a.sent()) return [3 /*break*/, 4]; + return [4 /*yield*/, this.memory.write32(3758157104 /* DFSR */, 4 /* DFSR_DWTTRAP */ | 2 /* DFSR_BKPT */ | 1 /* DFSR_HALTED */)]; + case 2: + _a.sent(); + return [4 /*yield*/, this.debug.enable()]; + case 3: + _a.sent(); + _a.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Find out whether the CPU is halted. + */ + CortexM.prototype.isHalted = function () { + return __awaiter(this, void 0, void 0, function () { + var s; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.status()]; + case 1: + s = _a.sent(); + return [2 /*return*/, s.isHalted]; + } + }); + }); + }; + /** + * Read the current status of the CPU. + * + * @returns Object containing the contents of the `DHCSR` register, the `DFSR` register, and a boolean value + * stating the current halted state of the CPU. + */ + CortexM.prototype.status = function () { + return __awaiter(this, void 0, void 0, function () { + var prep, results, dhcsr, dfsr; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = new prepared_1.PreparedMemoryCommand(this.dev); + prep.read32(3758157296 /* DHCSR */); + prep.read32(3758157104 /* DFSR */); + return [4 /*yield*/, prep.go()]; + case 1: + results = _a.sent(); + dhcsr = results[0]; + dfsr = results[1]; + return [2 /*return*/, { + dfsr: dfsr, + dhscr: dhcsr, + isHalted: !!(dhcsr & 131072 /* S_HALT */), + }]; + } + }); + }); + }; + /** + * Reset the CPU core. This currently does a software reset - it is also technically possible to perform a 'hard' + * reset using the reset pin from the debugger. + */ + CortexM.prototype.reset = function (halt) { + if (halt === void 0) { halt = false; } + return __awaiter(this, void 0, void 0, function () { + var demcr; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!halt) return [3 /*break*/, 7]; + return [4 /*yield*/, this.halt()]; + case 1: + _a.sent(); + return [4 /*yield*/, this.memory.read32(3758157308 /* DEMCR */)]; + case 2: + demcr = _a.sent(); + return [4 /*yield*/, this.memory.write32(3758157308 /* DEMCR */, demcr | 1 /* DEMCR_VC_CORERESET */)]; + case 3: + _a.sent(); + return [4 /*yield*/, this.softwareReset()]; + case 4: + _a.sent(); + return [4 /*yield*/, this.waitForHalt()]; + case 5: + _a.sent(); + // Unset the VC_CORERESET bit + return [4 /*yield*/, this.memory.write32(3758157308 /* DEMCR */, demcr)]; + case 6: + // Unset the VC_CORERESET bit + _a.sent(); + return [3 /*break*/, 9]; + case 7: return [4 /*yield*/, this.softwareReset()]; + case 8: + _a.sent(); + _a.label = 9; + case 9: return [2 /*return*/]; + } + }); + }); + }; + /** + * Run specified machine code natively on the device. Assumes usual C calling conventions + * - returns the value of r0 once the program has terminated. The program _must_ terminate + * in order for this function to return. This can be achieved by placing a `bkpt` + * instruction at the end of the function. + * + * @param code array containing the machine code (32-bit words). + * @param address memory address at which to place the code. + * @param pc initial value of the program counter. + * @param lr initial value of the link register. + * @param sp initial value of the stack pointer. + * @param upload should we upload the code before running it. + * @param args set registers r0...rn before running code + * + * @returns A promise for the value of r0 on completion of the function call. + */ + CortexM.prototype.runCode = function (code, address, pc, lr, sp, upload) { + var args = []; + for (var _i = 6; _i < arguments.length; _i++) { + args[_i - 6] = arguments[_i]; + } + return __awaiter(this, void 0, void 0, function () { + var cmd, i; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + cmd = this.prepareCommand(); + cmd.halt(); + // Point the program counter to the start of the program + cmd.writeCoreRegister(15 /* PC */, pc); + cmd.writeCoreRegister(14 /* LR */, lr); + cmd.writeCoreRegister(13 /* SP */, sp); + for (i = 0; i < args.length; i++) { + cmd.writeCoreRegister(i, args[i]); + } + return [4 /*yield*/, cmd.go()]; + case 1: + _a.sent(); + if (!upload) return [3 /*break*/, 3]; + return [4 /*yield*/, this.memory.writeBlock(address, code)]; + case 2: + _a.sent(); + _a.label = 3; + case 3: + // Run the program and wait for halt + return [4 /*yield*/, this.resume()]; + case 4: + // Run the program and wait for halt + _a.sent(); + return [4 /*yield*/, this.waitForHalt(constants_1.DEFAULT_RUNCODE_TIMEOUT)]; + case 5: + _a.sent(); // timeout after 10s + return [4 /*yield*/, this.readCoreRegister(0 /* R0 */)]; + case 6: return [2 /*return*/, _a.sent()]; + } + }); + }); + }; + /** + * Spin until the chip has halted. + */ + CortexM.prototype.waitForHalt = function (timeout) { + if (timeout === void 0) { timeout = 0; } + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { + var running, _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + running = true; + if (timeout > 0) { + setTimeout(function () { + if (running) { + reject("waitForHalt timed out."); + running = false; + } + }, timeout); + } + _b.label = 1; + case 1: + _a = running; + if (!_a) return [3 /*break*/, 3]; + return [4 /*yield*/, this.isHalted()]; + case 2: + _a = !(_b.sent()); + _b.label = 3; + case 3: + if (!_a) return [3 /*break*/, 4]; + return [3 /*break*/, 1]; + case 4: + if (running) { + running = false; + resolve(); + } + return [2 /*return*/]; + } + }); + }); })]; + }); + }); + }; + CortexM.prototype.softwareReset = function () { + return __awaiter(this, void 0, void 0, function () { + var dhcsr; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.memory.write32(3758157068 /* NVIC_AIRCR */, 100270080 /* NVIC_AIRCR_VECTKEY */ | 4 /* NVIC_AIRCR_SYSRESETREQ */)]; + case 1: + _a.sent(); + return [4 /*yield*/, this.memory.read32(3758157296 /* DHCSR */)]; + case 2: + dhcsr = _a.sent(); + _a.label = 3; + case 3: + if (!((dhcsr & 33554432 /* S_RESET_ST */) !== 0)) return [3 /*break*/, 5]; + return [4 /*yield*/, this.memory.read32(3758157296 /* DHCSR */)]; + case 4: + dhcsr = _a.sent(); + return [3 /*break*/, 3]; + case 5: return [2 /*return*/]; + } + }); + }); + }; + return CortexM; +}()); +exports.CortexM = CortexM; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DEFAULT_RUNCODE_TIMEOUT = 10000 /* ms */; +exports.CPUID_IMPLEMENTER_MASK = 0xff000000; +exports.CPUID_IMPLEMENTER_POS = 24; +exports.CPUID_VARIANT_MASK = 0x00f00000; +exports.CPUID_VARIANT_POS = 20; +exports.CPUID_ARCHITECTURE_MASK = 0x000f0000; +exports.CPUID_ARCHITECTURE_POS = 16; +exports.CPUID_PARTNO_MASK = 0x0000fff0; +exports.CPUID_PARTNO_POS = 4; +exports.CPUID_REVISION_MASK = 0x0000000f; +exports.CPUID_REVISION_POS = 0; +exports.ISANames = new Map(); +exports.ISANames.set(12 /* ARMv6M */, "ARMv6M"); +exports.ISANames.set(15 /* ARMv7M */, "ARMv7M"); +exports.CoreNames = new Map(); +exports.CoreNames.set(3104 /* CortexM0 */, "Cortex-M0"); +exports.CoreNames.set(3105 /* CortexM1 */, "Cortex-M1"); +exports.CoreNames.set(3107 /* CortexM3 */, "Cortex-M3"); +exports.CoreNames.set(3108 /* CortexM4 */, "Cortex-M4"); +exports.CoreNames.set(3168 /* CortexM0p */, "Cortex-M0+"); + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var cortex_1 = __webpack_require__(2); +exports.CortexM = cortex_1.CortexM; +var constants_1 = __webpack_require__(3); +exports.CoreNames = constants_1.CoreNames; +exports.ISANames = constants_1.ISANames; +var dap_1 = __webpack_require__(9); +exports.DAP = dap_1.default; +var FlashTarget_1 = __webpack_require__(12); +exports.FlashTargets = FlashTarget_1.FlashTargets; +exports.FlashTarget = FlashTarget_1.FlashTarget; +var FlashProgram_1 = __webpack_require__(15); +exports.FlashProgram = FlashProgram_1.FlashProgram; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var breakpoint_1 = __webpack_require__(6); +/** + * # Debug Interface + * + * Keeps track of breakpoints set on the target, as well as deciding whether to + * use a hardware breakpoint or a software breakpoint. + * + * ## Usage + * + * ```typescript + * const dbg = core.debug; + * + * await dbg.setBreakpoint(0x123456); + * + * // resume the core and wait for the breakpoint + * await core.resume(); + * await core.waitForHalt(); + * + * // step forward one instruction + * await dbg.step(); + * + * // remove the breakpoint + * await dbg.deleteBreakpoint(0x123456); + * ``` + */ +var Debug = (function () { + function Debug(core) { + this.core = core; + this.enabled = false; + this.availableHWBreakpoints = []; + this.breakpoints = new Map(); + } + Debug.prototype.init = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.setupFpb()]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Enable debugging on the target CPU + */ + Debug.prototype.enable = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.core.memory.write32(3758157296 /* DHCSR */, -1604386816 /* DBGKEY */ | 1 /* C_DEBUGEN */)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Set breakpoints at specified memory addresses. + * + * @param addrs An array of memory addresses at which to set breakpoints. + */ + Debug.prototype.setBreakpoint = function (addr) { + return __awaiter(this, void 0, void 0, function () { + var breakpoint, bkpt, regAddr; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.breakpoints.has(addr)) { + breakpoint = this.breakpoints.get(addr); + if (typeof breakpoint !== "number") { + // already enabled + console.warn("Breakpoint at " + addr.toString(16) + " already enabled."); + return [2 /*return*/]; + } + } + if (!(addr < 0x20000000)) return [3 /*break*/, 5]; + if (!(this.availableHWBreakpoints.length > 0)) return [3 /*break*/, 3]; + if (!!this.enabled) return [3 /*break*/, 2]; + console.log("enabling fpb"); + return [4 /*yield*/, this.setFpbEnabled(true)]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + regAddr = this.availableHWBreakpoints.pop(); + console.log("using regAddr=" + regAddr.toString(16)); + bkpt = new breakpoint_1.HWBreakpoint(regAddr, this.core, addr); + return [3 /*break*/, 4]; + case 3: + bkpt = new breakpoint_1.SWBreakpoint(this.core, addr); + _a.label = 4; + case 4: return [3 /*break*/, 6]; + case 5: + bkpt = new breakpoint_1.SWBreakpoint(this.core, addr); + _a.label = 6; + case 6: return [4 /*yield*/, bkpt.set()]; + case 7: + _a.sent(); + this.breakpoints.set(addr, bkpt); + return [2 /*return*/]; + } + }); + }); + }; + Debug.prototype.deleteBreakpoint = function (addr) { + return __awaiter(this, void 0, void 0, function () { + var bkpt; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!this.breakpoints.has(addr)) return [3 /*break*/, 3]; + bkpt = this.breakpoints.get(addr); + if (!(typeof bkpt !== "number")) return [3 /*break*/, 2]; + return [4 /*yield*/, bkpt.clear()]; + case 1: + _a.sent(); + if (bkpt instanceof breakpoint_1.HWBreakpoint) { + // return the register address to the pool + this.availableHWBreakpoints.push(bkpt.regAddr); + } + _a.label = 2; + case 2: + this.breakpoints.delete(addr); + return [3 /*break*/, 4]; + case 3: + console.warn("Breakpoint at " + addr.toString(16) + " does not exist."); + _a.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + }; + /** + * Step the processor forward by one instruction. + */ + Debug.prototype.step = function () { + return __awaiter(this, void 0, void 0, function () { + var dhcsr, interruptsMasked; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.core.memory.read32(3758157296 /* DHCSR */)]; + case 1: + dhcsr = _a.sent(); + if (!(dhcsr & (4 /* C_STEP */ | 2 /* C_HALT */))) { + console.error("Target is not halted."); + return [2 /*return*/]; + } + interruptsMasked = (8 /* C_MASKINTS */ & dhcsr) !== 0; + if (!!interruptsMasked) return [3 /*break*/, 3]; + return [4 /*yield*/, this.core.memory.write32(3758157296 /* DHCSR */, -1604386816 /* DBGKEY */ | + 1 /* C_DEBUGEN */ | + 2 /* C_HALT */ | + 8 /* C_MASKINTS */)]; + case 2: + _a.sent(); + _a.label = 3; + case 3: return [4 /*yield*/, this.core.memory.write32(3758157296 /* DHCSR */, -1604386816 /* DBGKEY */ | + 1 /* C_DEBUGEN */ | + 8 /* C_MASKINTS */ | + 4 /* C_STEP */)]; + case 4: + _a.sent(); + return [4 /*yield*/, this.core.waitForHalt()]; + case 5: + _a.sent(); + return [4 /*yield*/, this.core.memory.write32(3758157296 /* DHCSR */, -1604386816 /* DBGKEY */ | + 1 /* C_DEBUGEN */ | + 2 /* C_HALT */)]; + case 6: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Set up (and disable) the Flash Patch & Breakpoint unit. It will be enabled when + * the first breakpoint is set. + * + * Also reads the number of available hardware breakpoints. + */ + Debug.prototype.setupFpb = function () { + return __awaiter(this, void 0, void 0, function () { + var fpcr, nbCode, nbLit, i; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.core.memory.read32(3758104576 /* FP_CTRL */)]; + case 1: + fpcr = _a.sent(); + nbCode = ((fpcr >> 8) & 0x70) | ((fpcr >> 4) & 0xf); + nbLit = (fpcr >> 7) & 0xf; + this.totalHWBreakpoints = nbCode; + console.debug(nbCode + " hardware breakpoints, " + nbLit + " literal comparators"); + return [4 /*yield*/, this.setFpbEnabled(false)]; + case 2: + _a.sent(); + i = 0; + _a.label = 3; + case 3: + if (!(i < nbCode)) return [3 /*break*/, 6]; + this.availableHWBreakpoints.push(3758104584 /* FP_COMP0 */ + (4 * i)); + return [4 /*yield*/, this.core.memory.write32(3758104584 /* FP_COMP0 */ + (i * 4), 0)]; + case 4: + _a.sent(); + _a.label = 5; + case 5: + i++; + return [3 /*break*/, 3]; + case 6: return [2 /*return*/]; + } + }); + }); + }; + /** + * Enable or disable the Flash Patch and Breakpoint unit (FPB). + * + * @param enabled + */ + Debug.prototype.setFpbEnabled = function (enabled) { + if (enabled === void 0) { enabled = true; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this.enabled = enabled; + return [4 /*yield*/, this.core.memory.write32(3758104576 /* FP_CTRL */, 2 /* FP_CTRL_KEY */ | (enabled ? 1 : 0))]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + return Debug; +}()); +exports.Debug = Debug; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var HWBreakpoint = (function () { + function HWBreakpoint(regAddr, parent, addr) { + this.regAddr = regAddr; + this.parent = parent; + this.addr = addr; + } + HWBreakpoint.prototype.set = function () { + return __awaiter(this, void 0, void 0, function () { + var bpMatch; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + bpMatch = ((this.addr & 0x2) ? 2 : 1) << 30; + return [4 /*yield*/, this.parent.memory.write32(this.regAddr, this.addr & 0x1ffffffc | bpMatch | 1)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + HWBreakpoint.prototype.clear = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + /* clear hardware breakpoint */ + return [4 /*yield*/, this.parent.memory.write32(this.regAddr, 0)]; + case 1: + /* clear hardware breakpoint */ + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + return HWBreakpoint; +}()); +exports.HWBreakpoint = HWBreakpoint; +var SWBreakpoint = (function () { + function SWBreakpoint(parent, addr) { + this.parent = parent; + this.addr = addr; + } + SWBreakpoint.prototype.set = function () { + return __awaiter(this, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + // read the instruction from the CPU... + _a = this; + return [4 /*yield*/, this.parent.memory.read16(this.addr)]; + case 1: + // read the instruction from the CPU... + _a.instruction = _b.sent(); + return [4 /*yield*/, this.parent.memory.write16(this.addr, SWBreakpoint.BKPT_INSTRUCTION)]; + case 2: + _b.sent(); + return [2 /*return*/]; + } + }); + }); + }; + SWBreakpoint.prototype.clear = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + /* clear hardware breakpoint */ + return [4 /*yield*/, this.parent.memory.write16(this.addr, this.instruction)]; + case 1: + /* clear hardware breakpoint */ + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + SWBreakpoint.BKPT_INSTRUCTION = 0xbe00; + return SWBreakpoint; +}()); +exports.SWBreakpoint = SWBreakpoint; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var util_1 = __webpack_require__(0); +var prepared_1 = __webpack_require__(1); +/** + * # Memory Interface + * + * Controls access to the target's memory. + * + * ## Usage + * + * Using an instance of `CortexM`, as described before, we can simply read and + * write numbers to memory as follows: + * + * ```typescript + * const mem = core.memory; + * + * // NOTE: the address parameter must be word (4-byte) aligned. + * await mem.write32(0x200000, 12345); + * const val = await mem.read32(0x200000); + * + * // val === 12345 + * + * // NOTE: the address parameter must be half-word (2-byte) aligned + * await mem.write16(0x2000002, 65534); + * const val16 = await mem.read16(0x2000002); + * + * // val16 === 65534 + * ``` + * + * To write a larger block of memory, we can use `readBlock` and `writeBlock`. Again, + * these blocks must be written to word-aligned addresses in memory. + * + * ```typescript + * const data = new Uint32Array([0x1234, 0x5678, 0x9ABC, 0xDEF0]); + * await mem.writeBlock(0x200000, data); + * + * const readData = await mem.readBlock(0x200000, data.length, 0x100); + * ``` + * + * ## See also + * + * `PreparedMemoryCommand` provides an equivalent API with better performance (in some + * cases) by enabling batched memory operations. + */ +var Memory = (function () { + function Memory(dev) { + this.dev = dev; + } + /** + * Write a 32-bit word to the specified (word-aligned) memory address. + * + * @param addr Memory address to write to + * @param data Data to write (values above 2**32 will be truncated) + */ + Memory.prototype.write32 = function (addr, data) { + return __awaiter(this, void 0, void 0, function () { + var prep; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = this.dev.prepareCommand(); + prep.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 2 /* CSW_SIZE32 */); + prep.writeAp(4 /* TAR */, addr); + prep.writeAp(12 /* DRW */, data); + return [4 /*yield*/, prep.go()]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Write a 16-bit word to the specified (half word-aligned) memory address. + * + * @param addr Memory address to write to + * @param data Data to write (values above 2**16 will be truncated) + */ + Memory.prototype.write16 = function (addr, data) { + return __awaiter(this, void 0, void 0, function () { + var prep; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + data = data << ((addr & 0x02) << 3); + prep = this.dev.prepareCommand(); + prep.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 1 /* CSW_SIZE16 */); + prep.writeAp(4 /* TAR */, addr); + prep.writeAp(12 /* DRW */, data); + return [4 /*yield*/, prep.go()]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Read a 32-bit word from the specified (word-aligned) memory address. + * + * @param addr Memory address to read from. + */ + Memory.prototype.read32 = function (addr) { + return __awaiter(this, void 0, void 0, function () { + var prep, e_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = this.dev.prepareCommand(); + prep.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 2 /* CSW_SIZE32 */); + prep.writeAp(4 /* TAR */, addr); + prep.readAp(12 /* DRW */); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 6]); + return [4 /*yield*/, prep.go()]; + case 2: return [2 /*return*/, (_a.sent())[0]]; + case 3: + e_1 = _a.sent(); + // transfer wait, try again. + return [4 /*yield*/, util_1.delay(100)]; + case 4: + // transfer wait, try again. + _a.sent(); + return [4 /*yield*/, this.read32(addr)]; + case 5: return [2 /*return*/, _a.sent()]; + case 6: return [2 /*return*/]; + } + }); + }); + }; + /** + * Read a 16-bit word from the specified (half word-aligned) memory address. + * + * @param addr Memory address to read from. + */ + Memory.prototype.read16 = function (addr) { + return __awaiter(this, void 0, void 0, function () { + var prep, val, e_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = this.dev.prepareCommand(); + prep.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 1 /* CSW_SIZE16 */); + prep.writeAp(4 /* TAR */, addr); + prep.readAp(12 /* DRW */); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 6]); + return [4 /*yield*/, prep.go()]; + case 2: + val = (_a.sent())[0]; + return [3 /*break*/, 6]; + case 3: + e_2 = _a.sent(); + // transfer wait, try again. + return [4 /*yield*/, util_1.delay(100)]; + case 4: + // transfer wait, try again. + _a.sent(); + return [4 /*yield*/, this.read16(addr)]; + case 5: + val = _a.sent(); + return [3 /*break*/, 6]; + case 6: + val = (val >> ((addr & 0x02) << 3) & 0xffff); + return [2 /*return*/, val]; + } + }); + }); + }; + /** + * Reads a block of memory from the specified memory address. + * + * @param addr Address to read from + * @param words Number of words to read + * @param pageSize Memory page size + */ + Memory.prototype.readBlock = function (addr, words, pageSize) { + return __awaiter(this, void 0, void 0, function () { + var bufs, end, ptr, nextptr, len, _a, _b, result; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + bufs = []; + end = addr + words * 4; + ptr = addr; + _c.label = 1; + case 1: + if (!(ptr < end)) return [3 /*break*/, 3]; + nextptr = ptr + pageSize; + if (ptr === addr) { + nextptr &= ~(pageSize - 1); + } + len = Math.min(nextptr - ptr, end - ptr); + util_1.assert((len & 3) === 0); + _b = (_a = bufs).push; + return [4 /*yield*/, this.readBlockCore(ptr, len >> 2)]; + case 2: + _b.apply(_a, [_c.sent()]); + ptr = nextptr; + return [3 /*break*/, 1]; + case 3: + result = util_1.bufferConcat(bufs); + return [2 /*return*/, result.subarray(0, words * 4)]; + } + }); + }); + }; + /** + * Write a block of memory to the specified memory address. + * + * @param addr Memory address to write to. + * @param words Array of 32-bit words to write to memory. + */ + Memory.prototype.writeBlock = function (addr, words) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if (words.length === 0) { + return [2 /*return*/]; + } + return [2 /*return*/, this.writeBlockCore(addr, words)]; + }); + }); + }; + Memory.prototype.prepareCommand = function () { + return new prepared_1.PreparedMemoryCommand(this.dev); + }; + Memory.prototype.readBlockCore = function (addr, words) { + return __awaiter(this, void 0, void 0, function () { + var prep, lastSize, blocks, i, b; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = this.dev.prepareCommand(); + prep.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 2 /* CSW_SIZE32 */); + prep.writeAp(4 /* TAR */, addr); + return [4 /*yield*/, prep.go()]; + case 1: + _a.sent(); + lastSize = words % 15; + if (lastSize === 0) { + lastSize = 15; + } + blocks = []; + i = 0; + _a.label = 2; + case 2: + if (!(i < Math.ceil(words / 15))) return [3 /*break*/, 5]; + return [4 /*yield*/, this.dev.readRegRepeat(util_1.apReg(12 /* DRW */, 2 /* READ */), i === blocks.length - 1 ? lastSize : 15)]; + case 3: + b = _a.sent(); + blocks.push(b); + _a.label = 4; + case 4: + i++; + return [3 /*break*/, 2]; + case 5: return [2 /*return*/, util_1.bufferConcat(blocks).subarray(0, words * 4)]; + } + }); + }); + }; + Memory.prototype.writeBlockCore = function (addr, words) { + return __awaiter(this, void 0, void 0, function () { + var prep, e_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 7]); + prep = this.dev.prepareCommand(); + prep.writeAp(0 /* CSW */, 587202640 /* CSW_VALUE */ | 2 /* CSW_SIZE32 */); + prep.writeAp(4 /* TAR */, addr); + prep.writeRegRepeat(util_1.apReg(12 /* DRW */, 0 /* WRITE */), words); + return [4 /*yield*/, prep.go()]; + case 1: + _a.sent(); + return [3 /*break*/, 7]; + case 2: + e_3 = _a.sent(); + if (!e_3.dapWait) return [3 /*break*/, 5]; + console.debug("transfer wait, write block"); + return [4 /*yield*/, util_1.delay(100)]; + case 3: + _a.sent(); + return [4 /*yield*/, this.writeBlockCore(addr, words)]; + case 4: return [2 /*return*/, _a.sent()]; + case 5: throw e_3; + case 6: return [3 /*break*/, 7]; + case 7: return [2 /*return*/]; + } + }); + }); + }; + return Memory; +}()); +exports.Memory = Memory; + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var prepared_1 = __webpack_require__(1); +/** + * # Cortex M: Prepared Command + * + * Allows batching of Cortex M-related commands, such as writing to a register, + * halting and resuming the core. + * + * ## Example + * + * When preparing the sequence of commands, we can use the same API to prepare + * a command as we would to execute them immediately. + * + * ```typescript + * // Note that only the .go method is asynchronous. + * + * const prep = core.prepareCommand(); + * prep.writeCoreRegister(CortexReg.R0, 0x1000); + * prep.writeCoreRegister(CortexReg.R1, 0x0); + * prep.writeCoreRegister(CortexReg.PC, 0x2000000); + * prep.resume(); + * ``` + * + * We can then execute them as efficiently as possible by combining them together + * and executing them like so. + * + * ```typescript + * await prep.go(); + * ``` + * + * The code above is equivalent to the following _non-prepared_ command: + * + * ```typescript + * await core.writeCoreRegister(CortexReg.R0, 0x1000); + * await core.writeCoreRegister(CortexReg.R1, 0x0); + * await core.writeCoreRegister(CortexReg.PC, 0x2000000); + * await core.resume(); + * ``` + * + * Since the batched version of this code avoids making three round-trips to the + * target, we are able to significantly improve performance. This is especially + * noticable when uploading a binary to flash memory, where are large number of + * repetetive commands are being used. + * + * ## Explanation + * + * For a detailed explanation of why prepared commands are used in DAP.js, see the + * documentation for `PreparedDapCommand`. + */ +var PreparedCortexMCommand = (function () { + function PreparedCortexMCommand(dap) { + this.cmd = new prepared_1.PreparedMemoryCommand(dap); + } + /** + * Schedule a 32-bit integer to be written to a core register. + * + * @param no Core register to be written. + * @param val Value to write. + */ + PreparedCortexMCommand.prototype.writeCoreRegister = function (no, val) { + this.cmd.write32(3758157304 /* DCRDR */, val); + this.cmd.write32(3758157300 /* DCRSR */, no | 65536 /* DCRSR_REGWnR */); + }; + /** + * Schedule a halt command to be written to the CPU. + */ + PreparedCortexMCommand.prototype.halt = function () { + this.cmd.write32(3758157296 /* DHCSR */, -1604386816 /* DBGKEY */ | 1 /* C_DEBUGEN */ | 2 /* C_HALT */); + }; + /** + * Schedule a resume command to be written to the CPU. + */ + PreparedCortexMCommand.prototype.resume = function () { + this.cmd.write32(3758157104 /* DFSR */, 4 /* DFSR_DWTTRAP */ | 2 /* DFSR_BKPT */ | 1 /* DFSR_HALTED */); + }; + /** + * Execute all scheduled commands. + */ + PreparedCortexMCommand.prototype.go = function () { + return __awaiter(this, void 0, void 0, function () { + var v; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.cmd.go()]; + case 1: + v = _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + return PreparedCortexMCommand; +}()); +exports.PreparedCortexMCommand = PreparedCortexMCommand; + + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var prepared_1 = __webpack_require__(10); +var cmsis_dap_1 = __webpack_require__(11); +var util_1 = __webpack_require__(0); +var DAP = (function () { + function DAP(device) { + this.device = device; + this.dap = new cmsis_dap_1.CMSISDAP(device); + } + DAP.prototype.reconnect = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.dap.disconnect()]; + case 1: + _a.sent(); + return [4 /*yield*/, util_1.delay(100)]; + case 2: + _a.sent(); + return [4 /*yield*/, this.init()]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + DAP.prototype.init = function () { + return __awaiter(this, void 0, void 0, function () { + var n, prep, m, v; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.dap.connect()]; + case 1: + _a.sent(); + return [4 /*yield*/, this.readDp(0 /* IDCODE */)]; + case 2: + n = _a.sent(); + this.idcode = n; + prep = this.prepareCommand(); + prep.writeReg(0 /* DP_0x0 */, 1 << 2); // clear sticky error + prep.writeDp(2 /* SELECT */, 0); + prep.writeDp(1 /* CTRL_STAT */, 1073741824 /* CSYSPWRUPREQ */ | 268435456 /* CDBGPWRUPREQ */); + m = 536870912 /* CDBGPWRUPACK */ | 2147483648 /* CSYSPWRUPACK */; + prep.readDp(1 /* CTRL_STAT */); + return [4 /*yield*/, prep.go()]; + case 3: + v = (_a.sent())[0]; + _a.label = 4; + case 4: + if (!((v & m) !== m)) return [3 /*break*/, 6]; + return [4 /*yield*/, this.readDp(1 /* CTRL_STAT */)]; + case 5: + v = _a.sent(); + return [3 /*break*/, 4]; + case 6: + prep = this.prepareCommand(); + prep.writeDp(1 /* CTRL_STAT */, (1073741824 /* CSYSPWRUPREQ */ | + 268435456 /* CDBGPWRUPREQ */ | + 0 /* TRNNORMAL */ | + 3840 /* MASKLANE */)); + prep.writeDp(2 /* SELECT */, 0); + prep.readAp(252 /* IDR */); + return [4 /*yield*/, prep.go()]; + case 7: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + DAP.prototype.writeReg = function (regId, val) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.regOp(regId, val)]; + }); + }); + }; + DAP.prototype.readReg = function (regId) { + return __awaiter(this, void 0, void 0, function () { + var buf, v; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.regOp(regId, null)]; + case 1: + buf = _a.sent(); + v = util_1.readUInt32LE(buf, 3); + return [2 /*return*/, v]; + } + }); + }); + }; + DAP.prototype.prepareCommand = function () { + return new prepared_1.PreparedDapCommand(this.dap); + }; + DAP.prototype.readDp = function (addr) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.readReg(addr)]; + }); + }); + }; + DAP.prototype.readAp = function (addr) { + return __awaiter(this, void 0, void 0, function () { + var prep; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + prep = this.prepareCommand(); + prep.writeDp(2 /* SELECT */, util_1.bank(addr)); + prep.readReg(util_1.apReg(addr, 2 /* READ */)); + return [4 /*yield*/, prep.go()]; + case 1: return [2 /*return*/, (_a.sent())[0]]; + } + }); + }); + }; + DAP.prototype.writeDp = function (addr, data) { + if (addr === 2 /* SELECT */) { + if (data === this.dpSelect) { + return Promise.resolve(); + } + this.dpSelect = data; + } + return this.writeReg(addr, data); + }; + DAP.prototype.writeAp = function (addr, data) { + return __awaiter(this, void 0, void 0, function () { + var prep; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (addr === 0 /* CSW */) { + if (data === this.csw) { + return [2 /*return*/, Promise.resolve()]; + } + this.csw = data; + } + prep = this.prepareCommand(); + prep.writeDp(2 /* SELECT */, util_1.bank(addr)); + prep.writeReg(util_1.apReg(addr, 0 /* WRITE */), data); + return [4 /*yield*/, prep.go()]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + DAP.prototype.close = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.device.close()]; + }); + }); + }; + DAP.prototype.readRegRepeat = function (regId, cnt) { + return __awaiter(this, void 0, void 0, function () { + var request, sendargs, i, buf; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + util_1.assert(cnt <= 15); + request = util_1.regRequest(regId); + sendargs = [0, cnt]; + for (i = 0; i < cnt; ++i) { + sendargs.push(request); + } + return [4 /*yield*/, this.dap.cmdNums(5 /* DAP_TRANSFER */, sendargs)]; + case 1: + buf = _a.sent(); + if (buf[1] !== cnt) { + throw new Error(("(many) Bad #trans " + buf[1])); + } + else if (buf[2] !== 1) { + throw new Error(("(many) Bad transfer status " + buf[2])); + } + return [2 /*return*/, buf.subarray(3, 3 + cnt * 4)]; + } + }); + }); + }; + DAP.prototype.writeRegRepeat = function (regId, data) { + return __awaiter(this, void 0, void 0, function () { + var remainingLength, request, sendargs, buf; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + remainingLength = 64 - 1 - 1 - 2 - 1; + util_1.assert(data.length <= remainingLength / 4); + request = util_1.regRequest(regId, true); + sendargs = [0, data.length, 0, request]; + data.forEach(function (d) { + // separate d into bytes + util_1.addInt32(sendargs, d); + }); + return [4 /*yield*/, this.dap.cmdNums(6 /* DAP_TRANSFER_BLOCK */, sendargs)]; + case 1: + buf = _a.sent(); + if (buf[3] !== 1) { + throw new Error(("(many-wr) Bad transfer status " + buf[2])); + } + return [2 /*return*/]; + } + }); + }); + }; + DAP.prototype.regOp = function (regId, val) { + return __awaiter(this, void 0, void 0, function () { + var request, sendargs, buf; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + request = util_1.regRequest(regId, val !== null); + sendargs = [0, 1, request]; + if (val !== null) { + util_1.addInt32(sendargs, val); + } + return [4 /*yield*/, this.dap.cmdNums(5 /* DAP_TRANSFER */, sendargs)]; + case 1: + buf = _a.sent(); + if (buf[1] !== 1) { + console.error("Make sure you have initialised the DAP connection."); + throw new Error(("Bad #trans " + buf[1])); + } + else if (buf[2] !== 1) { + if (buf[2] === 2) { + throw new Error(("Transfer wait")); + } + throw new Error(("Bad transfer status " + buf[2])); + } + return [2 /*return*/, buf]; + } + }); + }); + }; + return DAP; +}()); +exports.default = DAP; + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var util_1 = __webpack_require__(0); +/** + * # Prepared DAP Command + * + * Batches together multiple Debug Access Port (DAP) commands into one (or more) + * CMSIS-DAP Transfers that can be written together to improve link utilisation. + * + * > **NOTE:** this will not normally need to be used by applications or libraries + * > depending on DAP.js. + * + * ## Architecture + * + * - `PreparedDapCommand` keeps a list of CMSIS-DAP `Transfer` commands. + * - Every time an action is scheduled (writing to or reading from a DP or AP register), + * we check to see if there is any remaining room in the current batch, starting a new + * batch if none is available. + * - When `go` is called, the batches are executed sequentially (so DAP commands are + * executed in the order they were added). + * + * ### Reading Values + * + * Writing values to registers is relatively straight forward, however mixing register + * reads and writes together requires us to keep track of how many commands in + * each batch are read commands. + * + * Once data has successfully been read back from the target, the values read are assembled + * into an array, and returned in the order they requested. This allows `PreparedDapCommand`s + * to be used higher up the stack in places where multiple independent read operations take + * place sequentially. + * + * ### Constructing CMSIS-DAP Commands + * + * We keep track of the number of commands in each batch, so that we can fill in the command + * count field of the `DAP_Transfer`. + */ +var PreparedDapCommand = (function () { + function PreparedDapCommand(dap) { + this.dap = dap; + this.commands = [[0, 1]]; + this.commandCounts = [0]; + this.currentCommand = 0; + this.readCounts = [0]; + } + /** + * Schedule a value to be written to an AP or DP register. + * + * @param regId register ID to be written to + * @param value value to be written + */ + PreparedDapCommand.prototype.writeReg = function (regId, value) { + var request = util_1.regRequest(regId, true); + if (this.commands[this.currentCommand].length + 5 > 64) { + // start a new command + this.commands.push([0, 1]); + this.commandCounts.push(0); + this.readCounts.push(0); + this.currentCommand++; + } + this.commands[this.currentCommand].push(request); + util_1.addInt32(this.commands[this.currentCommand], value); + this.commandCounts[this.currentCommand]++; + }; + /** + * Schedule a value to be read from an AP or DP register. + * @param regId register to read from + */ + PreparedDapCommand.prototype.readReg = function (regId) { + var request = util_1.regRequest(regId, false); + if (this.commands[this.currentCommand].length + 1 > 64) { + // start a new command + this.commands.push([0, 1]); + this.commandCounts.push(0); + this.readCounts.push(0); + this.currentCommand++; + } + this.commands[this.currentCommand].push(request); + this.commandCounts[this.currentCommand]++; + this.readCounts[this.currentCommand]++; + }; + /** + * Schedule multiple values to be written to the same register. + * + * **TODO:** figure out dynamically whether it's better to use DAP_TransferBlock vs + * DAP_Transfer. We should be able to fill up the remaining space in a Transfer + * and then start a TransferBlock _if_ we can fit in _13 or more_ values into the + * TransferBlock. However, the gains from this are marginal unless we're using much + * larger packet sizes than 64 bytes. + * + * @param regId register to write to repeatedly + * @param data array of 32-bit values to be written + */ + PreparedDapCommand.prototype.writeRegRepeat = function (regId, data) { + var _this = this; + // fill up the rest of the command we have left + data.forEach(function (cmd) { + _this.writeReg(regId, cmd); + }); + }; + /** + * Asynchronously execute the commands scheduled. + */ + PreparedDapCommand.prototype.go = function () { + return __awaiter(this, void 0, void 0, function () { + var v, i, command, results, i, result, j; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + v = []; + for (i = 0; i < this.commands.length; i++) { + command = this.commands[i]; + command[1] = this.commandCounts[i]; + } + return [4 /*yield*/, this.dap.sendTransfers(this.commands)]; + case 1: + results = _a.sent(); + for (i = 0; i < this.commands.length; i++) { + result = results[i]; + for (j = 0; j < this.readCounts[i]; j++) { + v.push(util_1.readUInt32LE(result, 3 + 4 * j)); + } + } + return [2 /*return*/, v]; + } + }); + }); + }; + /** + * Schedule a value to be written to a DP register + * + * @param addr Address to write to + * @param data Data to be written + */ + PreparedDapCommand.prototype.writeDp = function (addr, data) { + if (addr === 2 /* SELECT */) { + if (data === this.dpSelect) { + return Promise.resolve(); + } + this.dpSelect = data; + } + return this.writeReg(addr, data); + }; + /** + * Schedule a value to be written to an AP register + * + * @param addr Address to write to + * @param data Data to be written + */ + PreparedDapCommand.prototype.writeAp = function (addr, data) { + this.writeDp(2 /* SELECT */, util_1.bank(addr)); + if (addr === 0 /* CSW */) { + if (data === this.csw) { + return Promise.resolve(); + } + this.csw = data; + } + this.writeReg(util_1.apReg(addr, 0 /* WRITE */), data); + }; + /** + * Schedule a DP register to read from + * + * @param addr Address to read from + */ + PreparedDapCommand.prototype.readDp = function (addr) { + return this.readReg(addr); + }; + /** + * Schedule an AP register to read from + * + * @param addr Address to read from + */ + PreparedDapCommand.prototype.readAp = function (addr) { + this.writeDp(2 /* SELECT */, util_1.bank(addr)); + return this.readReg(util_1.apReg(addr, 2 /* READ */)); + }; + return PreparedDapCommand; +}()); +exports.PreparedDapCommand = PreparedDapCommand; + + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var util_1 = __webpack_require__(0); +var CMSISDAP = (function () { + function CMSISDAP(hid) { + this.maxSent = 1; + this.hid = hid; + } + CMSISDAP.prototype.resetTarget = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.cmdNums(10 /* DAP_RESET_TARGET */, [])]; + }); + }); + }; + CMSISDAP.prototype.disconnect = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.cmdNums(3 /* DAP_DISCONNECT */, [])]; + }); + }); + }; + CMSISDAP.prototype.sendTransfers = function (commands) { + return __awaiter(this, void 0, void 0, function () { + var res, _i, commands_1, cmd, _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (this.hid.sendMany) + return [2 /*return*/, this.hid.sendMany(commands.map(function (cmd) { + cmd.unshift(5 /* DAP_TRANSFER */); + return Uint8Array.from(cmd); + })).then(function (bufs) { + for (var _i = 0, bufs_1 = bufs; _i < bufs_1.length; _i++) { + var buf = bufs_1[_i]; + if (buf[0] != 5 /* DAP_TRANSFER */) + throw new Error("Bad response for Transfer (many) -> " + buf[0]); + } + return bufs; + })]; + res = []; + _i = 0, commands_1 = commands; + _c.label = 1; + case 1: + if (!(_i < commands_1.length)) return [3 /*break*/, 4]; + cmd = commands_1[_i]; + _b = (_a = res).push; + return [4 /*yield*/, this.cmdNums(5 /* DAP_TRANSFER */, cmd)]; + case 2: + _b.apply(_a, [_c.sent()]); + _c.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/, res]; + } + }); + }); + }; + CMSISDAP.prototype.cmdNums = function (op, data) { + return __awaiter(this, void 0, void 0, function () { + var buf; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + data.unshift(op); + return [4 /*yield*/, this.send(data)]; + case 1: + buf = _a.sent(); + if (buf[0] !== op) { + throw new Error("Bad response for " + op + " -> " + buf[0]); + } + switch (op) { + case 2 /* DAP_CONNECT */: + case 0 /* DAP_INFO */: + case 5 /* DAP_TRANSFER */: + case 6 /* DAP_TRANSFER_BLOCK */: + break; + default: + if (buf[1] !== 0) { + throw new Error("Bad status for " + op + " -> " + buf[1]); + } + } + return [2 /*return*/, buf]; + } + }); + }); + }; + CMSISDAP.prototype.connect = function () { + return __awaiter(this, void 0, void 0, function () { + var v, buf; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + console.log("Connecting..."); + return [4 /*yield*/, this.info(254 /* PACKET_COUNT */)]; + case 1: + v = _a.sent(); + if (v) { + this.maxSent = v; + } + else { + throw new Error("DAP_INFO returned invalid packet count."); + } + return [4 /*yield*/, this.cmdNums(17 /* DAP_SWJ_CLOCK */, util_1.addInt32(null, 10000000))]; + case 2: + _a.sent(); + return [4 /*yield*/, this.cmdNums(2 /* DAP_CONNECT */, [0])]; + case 3: + buf = _a.sent(); + if (buf[1] !== 1) { + throw new Error("SWD mode not enabled."); + } + return [4 /*yield*/, this.cmdNums(17 /* DAP_SWJ_CLOCK */, util_1.addInt32(null, 10000000))]; + case 4: + _a.sent(); + return [4 /*yield*/, this.cmdNums(4 /* DAP_TRANSFER_CONFIGURE */, [0, 0x50, 0, 0, 0])]; + case 5: + _a.sent(); + return [4 /*yield*/, this.cmdNums(19 /* DAP_SWD_CONFIGURE */, [0])]; + case 6: + _a.sent(); + return [4 /*yield*/, this.jtagToSwd()]; + case 7: + _a.sent(); + console.log("Connected"); + return [2 /*return*/]; + } + }); + }); + }; + CMSISDAP.prototype.jtagToSwd = function () { + return __awaiter(this, void 0, void 0, function () { + var arrs, _i, arrs_1, arr; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + arrs = [ + [56, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], + [16, 0x9e, 0xe7], + [56, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], + [8, 0x00], + ]; + _i = 0, arrs_1 = arrs; + _a.label = 1; + case 1: + if (!(_i < arrs_1.length)) return [3 /*break*/, 4]; + arr = arrs_1[_i]; + return [4 /*yield*/, this.swjSequence(arr)]; + case 2: + _a.sent(); + _a.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + CMSISDAP.prototype.swjSequence = function (data) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.cmdNums(18 /* DAP_SWJ_SEQUENCE */, data)]; + }); + }); + }; + CMSISDAP.prototype.info = function (id) { + return __awaiter(this, void 0, void 0, function () { + var buf; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.cmdNums(0 /* DAP_INFO */, [id])]; + case 1: + buf = _a.sent(); + if (buf[1] === 0) { + return [2 /*return*/, null]; + } + switch (id) { + case 240 /* CAPABILITIES */: + case 254 /* PACKET_COUNT */: + case 255 /* PACKET_SIZE */: + if (buf[1] === 1) { + return [2 /*return*/, buf[2]]; + } + else if (buf[1] === 2) { + return [2 /*return*/, buf[3] << 8 | buf[2]]; + } + } + return [2 /*return*/, buf.subarray(2, buf[1] + 2 - 1)]; // .toString("utf8") + } + }); + }); + }; + CMSISDAP.prototype.send = function (command) { + return __awaiter(this, void 0, void 0, function () { + var array, response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + array = Uint8Array.from(command); + return [4 /*yield*/, this.hid.write(array.buffer)]; + case 1: + _a.sent(); + return [4 /*yield*/, this.hid.read()]; + case 2: + response = _a.sent(); + return [2 /*return*/, new Uint8Array(response.buffer)]; + } + }); + }); + }; + return CMSISDAP; +}()); +exports.CMSISDAP = CMSISDAP; + + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var cortex_1 = __webpack_require__(2); +var K64F_1 = __webpack_require__(13); +var NRF51_1 = __webpack_require__(14); +var analyzer = new Uint32Array([ + 0x2180468c, 0x2600b5f0, 0x4f2c2501, 0x447f4c2c, 0x1c2b0049, 0x425b4033, 0x40230872, 0x085a4053, + 0x425b402b, 0x40534023, 0x402b085a, 0x4023425b, 0x085a4053, 0x425b402b, 0x40534023, 0x402b085a, + 0x4023425b, 0x085a4053, 0x425b402b, 0x40534023, 0x402b085a, 0x4023425b, 0x085a4053, 0x425b402b, + 0x40534023, 0xc7083601, 0xd1d2428e, 0x2b004663, 0x4663d01f, 0x46b4009e, 0x24ff2701, 0x44844d11, + 0x1c3a447d, 0x88418803, 0x4351409a, 0xd0122a00, 0x22011856, 0x780b4252, 0x40533101, 0x009b4023, + 0x0a12595b, 0x42b1405a, 0x43d2d1f5, 0x4560c004, 0x2000d1e7, 0x2200bdf0, 0x46c0e7f8, 0x000000b6, + 0xedb88320, 0x00000044, +]); +var FlashTarget = (function (_super) { + __extends(FlashTarget, _super); + function FlashTarget(device, platform) { + var _this = _super.call(this, device) || this; + _this.platform = platform; + _this.inited = false; + return _this; + } + /** + * Initialise the flash driver on the chip. Must be called before any of the other + * flash-related methods. + */ + FlashTarget.prototype.flashInit = function () { + return __awaiter(this, void 0, void 0, function () { + var result; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.inited) { + return [2 /*return*/]; + } + // reset and halt + return [4 /*yield*/, this.reset(true)]; + case 1: + // reset and halt + _a.sent(); + // make sure we're in Thumb mode. + return [4 /*yield*/, this.writeCoreRegister(16 /* XPSR */, 1 << 24)]; + case 2: + // make sure we're in Thumb mode. + _a.sent(); + return [4 /*yield*/, this.writeCoreRegister(9 /* R9 */, this.platform.flashAlgo.staticBase)]; + case 3: + _a.sent(); + if (!this.platform.flashAlgo.analyzerSupported) return [3 /*break*/, 5]; + return [4 /*yield*/, this.memory.writeBlock(this.platform.flashAlgo.analyzerAddress, analyzer)]; + case 4: + _a.sent(); + _a.label = 5; + case 5: return [4 /*yield*/, this.runCode(this.platform.flashAlgo.instructions, this.platform.flashAlgo.loadAddress, this.platform.flashAlgo.pcInit, this.platform.flashAlgo.loadAddress + 1, this.platform.flashAlgo.stackPointer, true, 0, 0, 0, 0)]; + case 6: + result = _a.sent(); + this.inited = true; + return [2 /*return*/, result]; + } + }); + }); + }; + /** + * Erase _all_ data stored in flash on the chip. + */ + FlashTarget.prototype.eraseChip = function () { + return __awaiter(this, void 0, void 0, function () { + var result; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!!this.inited) return [3 /*break*/, 2]; + return [4 /*yield*/, this.flashInit()]; + case 1: + _a.sent(); + _a.label = 2; + case 2: return [4 /*yield*/, this.runCode(this.platform.flashAlgo.instructions, this.platform.flashAlgo.loadAddress, this.platform.flashAlgo.pcEraseAll, this.platform.flashAlgo.loadAddress + 1, this.platform.flashAlgo.stackPointer, false, 0, 0, 0)]; + case 3: + result = _a.sent(); + return [2 /*return*/, result]; + } + }); + }); + }; + /** + * Upload a program to flash memory on the chip. + * TODO: add a callback to provide progress data + * + * @param data Array of 32-bit integers to write to flash. + */ + FlashTarget.prototype.flash = function (data, address, progressCb) { + return __awaiter(this, void 0, void 0, function () { + var pageSizeWords, bufferAddress, flashStart, ptr, wordPtr, pageData, flashAddress; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!!this.inited) return [3 /*break*/, 2]; + return [4 /*yield*/, this.flashInit()]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + pageSizeWords = this.platform.flashAlgo.pageSize / 4; + bufferAddress = this.platform.flashAlgo.pageBuffers[0]; + flashStart = address || this.platform.flashAlgo.flashStart; + ptr = 0; + _a.label = 3; + case 3: + if (!(ptr < data.byteLength)) return [3 /*break*/, 6]; + wordPtr = ptr / 4; + pageData = data.subarray(wordPtr, wordPtr + pageSizeWords); + flashAddress = flashStart + ptr; + return [4 /*yield*/, this.memory.writeBlock(bufferAddress, pageData)]; + case 4: + _a.sent(); + return [4 /*yield*/, this.runCode(this.platform.flashAlgo.instructions, this.platform.flashAlgo.loadAddress, this.platform.flashAlgo.pcProgramPage, // pc + this.platform.flashAlgo.loadAddress + 1, // lr + this.platform.flashAlgo.stackPointer, // sp + /* upload? */ + false, + /* args */ + flashAddress, this.platform.flashAlgo.pageSize, bufferAddress)]; + case 5: + _a.sent(); + if (progressCb) { + progressCb(ptr / data.byteLength); + } + ptr += pageData.byteLength; + return [3 /*break*/, 3]; + case 6: + if (progressCb) { + progressCb(1.0); + } + return [2 /*return*/]; + } + }); + }); + }; + FlashTarget.prototype.program = function (program, progressCb) { + return __awaiter(this, void 0, void 0, function () { + var totalBytes, cumulativeBytes, startTime, _loop_1, this_1, _i, _a, section, endTime, elapsedTime, transferRate; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: return [4 /*yield*/, this.flashInit()]; + case 1: + _b.sent(); + return [4 /*yield*/, this.eraseChip()]; + case 2: + _b.sent(); + totalBytes = program.totalByteLength(); + cumulativeBytes = 0; + startTime = Date.now(); + _loop_1 = function (section) { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this_1.flash(section.data, section.address, function (progress) { + var sectionBytes = section.data.byteLength * progress; + progressCb((cumulativeBytes + sectionBytes) / totalBytes); + })]; + case 1: + _a.sent(); + cumulativeBytes += section.data.byteLength; + return [2 /*return*/]; + } + }); + }; + this_1 = this; + _i = 0, _a = program.sections; + _b.label = 3; + case 3: + if (!(_i < _a.length)) return [3 /*break*/, 6]; + section = _a[_i]; + return [5 /*yield**/, _loop_1(section)]; + case 4: + _b.sent(); + _b.label = 5; + case 5: + _i++; + return [3 /*break*/, 3]; + case 6: + endTime = Date.now(); + elapsedTime = endTime - startTime; + transferRate = totalBytes / elapsedTime; + console.debug("Transfer took " + elapsedTime / 1000 + " s"); + console.debug("Transfered " + totalBytes + " bytes at " + transferRate + " kB/s"); + return [4 /*yield*/, this.flashUnInit()]; + case 7: + _b.sent(); + progressCb(1.0); + return [2 /*return*/]; + } + }); + }); + }; + FlashTarget.prototype.flashUnInit = function () { + this.inited = false; + }; + return FlashTarget; +}(cortex_1.CortexM)); +exports.FlashTarget = FlashTarget; +exports.FlashTargets = new Map(); +exports.FlashTargets.set("0240", new K64F_1.K64F()); +exports.FlashTargets.set("9900", new NRF51_1.NRF51()); + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var K64F_FLASH_ALGO = { + analyzerAddress: 0x1ffff000, + analyzerSupported: true, + flashSize: 0x100000, + flashStart: 0x0, + // Flash algorithm as a hex string + instructions: new Uint32Array([ + 0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA, 0x2A001E52, 0x4770D1F2, + 0x4604b570, 0x4616460d, 0x5020f24c, 0x81c84932, 0x1028f64d, 0x460881c8, 0xf0208800, 0x80080001, + 0x4448482e, 0xf8dcf000, 0x2001b108, 0x2000bd70, 0x4601e7fc, 0x47702000, 0x4929b510, 0x44484827, + 0xf8b8f000, 0xb92c4604, 0x48242100, 0xf0004448, 0x4604f9a9, 0xf837f000, 0xbd104620, 0x4604b570, + 0x4448481e, 0x46214b1e, 0xf00068c2, 0x4605f85d, 0x481ab93d, 0x23004448, 0x68c24621, 0xf946f000, + 0xf0004605, 0x4628f820, 0xb5febd70, 0x460c4605, 0x46234616, 0x46294632, 0x44484810, 0xf8f8f000, + 0xb9674607, 0x22012000, 0x2000e9cd, 0x46224633, 0x90024629, 0x44484809, 0xf984f000, 0xf0004607, + 0x4638f802, 0x4807bdfe, 0xf4206840, 0xf5000070, 0x49040070, 0x47706048, 0x40052000, 0x00000004, + 0x6b65666b, 0x4001f000, 0x4a0e2070, 0x20807010, 0xbf007010, 0x7800480b, 0x280009c0, 0x4809d0fa, + 0xf0017801, 0xb1080020, 0x47702067, 0x0010f001, 0x2068b108, 0xf001e7f9, 0xb1080001, 0xe7f42069, + 0xe7f22000, 0x40020000, 0x4df0e92d, 0x460d4604, 0x469a4690, 0xf0004650, 0x4606f891, 0x4630b116, + 0x8df0e8bd, 0x46422310, 0x46204629, 0xf86cf000, 0xb10e4606, 0xe7f34630, 0x0008eb05, 0x68e01e47, + 0xf1f0fbb7, 0x7011fb00, 0x68e0b140, 0xf0f0fbb7, 0x0b01f100, 0xfb0068e0, 0x1e47f00b, 0x480be011, + 0x68004478, 0x20096005, 0x71c84909, 0xffacf7ff, 0x69a04606, 0x69a0b108, 0xb1064780, 0x68e0e003, + 0x42bd4405, 0xbf00d9eb, 0xe7c94630, 0x000002ec, 0x40020000, 0x4604b570, 0x4628460d, 0xf84ef000, + 0xb10e4606, 0xbd704630, 0x2004b90c, 0x2044e7fb, 0x71c84902, 0xff88f7ff, 0x0000e7f5, 0x40020000, + 0xb9094601, 0x47702004, 0x6cc04826, 0x6003f3c0, 0x447b4b25, 0x0010f833, 0xb90a0302, 0xe7f22064, + 0x60082000, 0x2002604a, 0x02c06088, 0x200060c8, 0x61486108, 0xbf006188, 0x4602e7e5, 0x2004b90a, + 0x61914770, 0xe7fb2000, 0x4604b530, 0x2004b90c, 0x1e58bd30, 0xb9104008, 0x40101e58, 0x2065b108, + 0x6820e7f6, 0xd8054288, 0x0500e9d4, 0x188d4428, 0xd20142a8, 0xe7eb2066, 0xe7e92000, 0x480b4601, + 0xd0014281, 0x4770206b, 0xe7fc2000, 0xb90b4603, 0x47702004, 0xd801290f, 0xd0012a04, 0xe7f82004, + 0xe7f62000, 0x40048000, 0x0000025a, 0x6b65666b, 0x41f0e92d, 0x46884607, 0x461d4614, 0x2004b914, + 0x81f0e8bd, 0x462a2308, 0x46384641, 0xffbcf7ff, 0xb10e4606, 0xe7f34630, 0x4812e01f, 0x68004478, + 0x8000f8c0, 0x490fcc01, 0x390c4479, 0x60486809, 0x490ccc01, 0x39184479, 0x60886809, 0x490a2007, + 0xf7ff71c8, 0x4606ff01, 0xb10869b8, 0x478069b8, 0xe004b106, 0x0808f108, 0x2d003d08, 0xbf00d1dd, + 0xe7cd4630, 0x000001b0, 0x40020000, 0x4dffe92d, 0x4682b082, 0x2310460c, 0x46504621, 0xf7ff9a04, + 0x4683ff83, 0x0f00f1bb, 0x4658d003, 0xe8bdb006, 0xe9da8df0, 0xfbb00101, 0x4260f7f1, 0x40084279, + 0x42a54245, 0x443dd100, 0xe0229e04, 0x0804eba5, 0xd90045b0, 0xea4f46b0, 0x90011018, 0x4478480f, + 0x60046800, 0x490e2001, 0x980171c8, 0x72c80a00, 0x72889801, 0x72489805, 0xfeb6f7ff, 0xf1bb4683, + 0xd0010f00, 0xe7d14658, 0x0608eba6, 0x443d4444, 0x2e00bf00, 0x2000d1da, 0x0000e7c8, 0x0000010e, + 0x40020000, 0x4604b570, 0xb90c460d, 0xbd702004, 0x49032040, 0x460871c8, 0xf7ff7185, 0xe7f6fe95, + 0x40020000, 0x4dffe92d, 0x4617460c, 0xe9dd461d, 0xf8ddb80c, 0xb91da038, 0xb0042004, 0x8df0e8bd, + 0x463a2304, 0x98004621, 0xff1ef7ff, 0xb10e4606, 0xe7f24630, 0x4814e022, 0x68004478, 0x20026004, + 0x71c84912, 0xf8804608, 0x490fb00b, 0x39144479, 0x68096828, 0xf7ff6088, 0x4606fe67, 0xf1b8b15e, + 0xd0010f00, 0x4000f8c8, 0x0f00f1ba, 0x2000d002, 0x0000f8ca, 0x1f3fe004, 0x1d241d2d, 0xd1da2f00, + 0x4630bf00, 0x0000e7c9, 0x00000074, 0x40020000, 0x00000000, 0x00080000, 0x00100000, 0x00200000, + 0x00400000, 0x00800000, 0x01000000, 0x01000000, 0x40020004, 0x00000000, + ]), + loadAddress: 0x20000000, + pageBuffers: [0x20003000, 0x20004000], + pageSize: 0x1000, + // Relative function addresses + pcEraseAll: 0x20000059, + pcEraseSector: 0x2000007D, + pcInit: 0x20000021, + // pcUnInit: 0x49, + pcProgramPage: 0x200000AB, + stackPointer: 0x20001000, + staticBase: 0x20000000 + 0x20 + 0x474, +}; +var K64F = (function () { + function K64F() { + this.flashAlgo = K64F_FLASH_ALGO; + } + K64F.prototype.overrideSecurityBits = function (address, data) { + var u8data = new Uint8Array(data.buffer); + // Kinetis security values and addresses + var SECURITY_START = 0x400; + var SECURITY_SIZE = 16; + var FPROT_ADDR = 0x408; + var FPROT_ADDR_END = 0x40c; + var FPROT_SIZE = 4; + var FSEC_ADDR = 0x40c; + var FSEC_VAL = 0xFE; + var FOPT_ADDR = 0x40d; + var FOPT_VAL = 0xFF; + var FEPROT_ADDR = 0x40e; + var FEPROT_VAL = 0xFF; + var FDPROT_ADDR = 0x40f; + var FDPROT_VAL = 0xFF; + if (address <= SECURITY_START && address + u8data.byteLength > SECURITY_START + SECURITY_SIZE) { + for (var i = FPROT_ADDR; i < FPROT_ADDR_END; i++) { + if (u8data[i - address] !== 0xff) { + u8data[i - address] = 0xff; + console.debug("FCF[" + (i - FPROT_ADDR) + "] at addr " + i + " changed to " + u8data[i - address]); + } + } + if (u8data[FSEC_ADDR - address] !== FSEC_VAL) { + u8data[FSEC_ADDR - address] = FSEC_VAL; + console.debug("FSEC at addr " + FSEC_ADDR + " changed to " + FSEC_VAL); + } + if (u8data[FOPT_ADDR - address] === 0x00) { + console.debug("FOPT set to restricted value 0x00"); + } + if (u8data[FEPROT_ADDR - address] !== FEPROT_VAL) { + u8data[FEPROT_ADDR - address] = FEPROT_VAL; + console.debug("FEPROT at addr " + FEPROT_ADDR + " changed to " + FEPROT_VAL); + } + if (u8data[FDPROT_ADDR - address] !== FDPROT_VAL) { + u8data[FDPROT_ADDR - address] = FDPROT_VAL; + console.debug("FDPROT at addr " + FDPROT_ADDR + " changed to " + FDPROT_VAL); + } + } + }; + return K64F; +}()); +exports.K64F = K64F; + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var NRF51_FLASH_ALGO = { + analyzerAddress: 0x20003000, + analyzerSupported: true, + beginData: 0x20002000, + flashSize: 0x40000, + flashStart: 0x0, + instructions: new Uint32Array([ + 0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA, 0x2A001E52, 0x4770D1F2, + 0x47702000, 0x47702000, 0x4c26b570, 0x60602002, 0x60e02001, 0x68284d24, 0xd00207c0, 0x60602000, + 0xf000bd70, 0xe7f6f82c, 0x4c1eb570, 0x60612102, 0x4288491e, 0x2001d302, 0xe0006160, 0x4d1a60a0, + 0xf81df000, 0x07c06828, 0x2000d0fa, 0xbd706060, 0x4605b5f8, 0x4813088e, 0x46142101, 0x4f126041, + 0xc501cc01, 0x07c06838, 0x1e76d006, 0x480dd1f8, 0x60412100, 0xbdf84608, 0xf801f000, 0x480ce7f2, + 0x06006840, 0xd00b0e00, 0x6849490a, 0xd0072900, 0x4a0a4909, 0xd00007c3, 0x1d09600a, 0xd1f90840, + 0x00004770, 0x4001e500, 0x4001e400, 0x10001000, 0x40010400, 0x40010500, 0x40010600, 0x6e524635, + 0x00000000, + ]), + loadAddress: 0x20000000, + minProgramLength: 4, + pageBuffers: [0x20002000, 0x20002400], + pageSize: 0x400, + pcEraseAll: 0x20000029, + pcEraseSector: 0x20000049, + pcInit: 0x20000021, + pcProgramPage: 0x20000071, + stackPointer: 0x20001000, + staticBase: 0x20000170, +}; +var NRF51 = (function () { + function NRF51() { + this.flashAlgo = NRF51_FLASH_ALGO; + } + NRF51.prototype.overrideSecurityBits = function (address, data) { + /* empty */ + }; + return NRF51; +}()); +exports.NRF51 = NRF51; + + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var util_1 = __webpack_require__(0); +var FlashSection = (function () { + function FlashSection(address, data) { + this.address = address; + this.data = data; + /* empty */ + } + FlashSection.prototype.toString = function () { + return this.data.byteLength + " bytes @ " + this.address.toString(16); + }; + return FlashSection; +}()); +exports.FlashSection = FlashSection; +var FlashProgram = (function () { + function FlashProgram(sections) { + this.sections = sections; + } + FlashProgram.fromIntelHex = function (hex) { + var lines = hex.split(/\n/); + var upperAddr = 0; + var startAddr = 0; + var current = null; + var chunks = []; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (line.substr(0, 1) !== ":") { + throw new Error("Invaild line in hex file: " + (i + 1)); + } + else { + var length_1 = parseInt(line.substr(1, 2), 16); + var addr = upperAddr + parseInt(line.substr(3, 4), 16); + var fieldType = parseInt(line.substr(7, 2), 16); + var data = line.substr(9, length_1 * 2); + if (fieldType === 0x00) { + if (current && addr !== startAddr + (current.length / 2)) { + // non-contiguous + var sectionData = util_1.hex2bin(current); + chunks.push(new FlashSection(startAddr, new Uint32Array(sectionData.buffer))); + current = ""; + startAddr = addr; + } + else if (!current) { + startAddr = addr; + current = ""; + } + current += data; + } + else if (fieldType === 0x01) { + // EOF + break; + } + else if (fieldType === 0x02) { + // extended segment address record + upperAddr = parseInt(data, 16) << 4; + } + else if (fieldType === 0x04) { + // extended linear address record + upperAddr = parseInt(data, 16) << 16; + } + } + } + return new FlashProgram(chunks); + }; + FlashProgram.fromBinary = function (addr, bin) { + return new FlashProgram([new FlashSection(addr, bin)]); + }; + FlashProgram.prototype.totalByteLength = function () { + return this.sections.map(function (s) { return s.data.byteLength; }).reduce(function (x, y) { return x + y; }); + }; + FlashProgram.prototype.toString = function () { + return this.sections.toString(); + }; + return FlashProgram; +}()); +exports.FlashProgram = FlashProgram; + + +/***/ }) +/******/ ]); +//# sourceMappingURL=dapjs.js.map \ No newline at end of file diff --git a/external/sha/.yotta.json b/external/sha/.yotta.json new file mode 100644 index 00000000..d7602538 --- /dev/null +++ b/external/sha/.yotta.json @@ -0,0 +1,6 @@ +{ + "build": { + "target": "bbc-microbit-classic-gcc,*", + "targetSetExplicitly": true + } +} diff --git a/external/sha/buildflash.sh b/external/sha/buildflash.sh new file mode 100755 index 00000000..ff40de0e --- /dev/null +++ b/external/sha/buildflash.sh @@ -0,0 +1,5 @@ +#!/bin/sh +yotta build +arm-none-eabi-objdump -d `find -name main.c.o` > disasm +node genapplet.js disasm Reset_Handler +rm disasm diff --git a/external/sha/genapplet.js b/external/sha/genapplet.js new file mode 100644 index 00000000..89fd3bee --- /dev/null +++ b/external/sha/genapplet.js @@ -0,0 +1,48 @@ +let fs = require("fs") +let s = fs.readFileSync(process.argv[2], "utf8") +let infun = false +let words = [] +for (let l of s.split(/\n/)) { + let m = /^00000000 <(.*)>:/.exec(l) + if (m && m[1] == process.argv[3]) infun = true + if (/^Disassembly/.test(l)) infun = false + if (!infun) continue + m = /^\s*[0-9a-f]+:\s+([0-9a-f]+)( ([0-9a-f]{4}))?\s+/.exec(l) + if (m) { + let n = m[1] + words.push(n) + if (m[3]) + words.push(m[3]) + if (n.length == 4 || n.length == 8) { + // ok + } else { + throw new Error() + } + } +} + +let ww = [] +let pref = "" +for (let w of words) { + if (w.length == 8) { + if (pref) throw new Error() + ww.push("0x" + w) + } else { + if (pref) { + ww.push("0x" + w + pref) + pref = "" + } else { + pref = w + } + } +} + +words = ww + +let r = "" +for (let i = 0; i < words.length; i++) { + if (i % 6 == 0) r += "\n" + r += words[i] + ", " +} + +console.log(r) \ No newline at end of file diff --git a/external/sha/module.json b/external/sha/module.json new file mode 100644 index 00000000..54dc0216 --- /dev/null +++ b/external/sha/module.json @@ -0,0 +1,9 @@ +{ + "name": "sha", + "version": "0.0.0", + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": {}, + "bin": "./source" +} diff --git a/external/sha/source/main.c b/external/sha/source/main.c new file mode 100644 index 00000000..af00cfa9 --- /dev/null +++ b/external/sha/source/main.c @@ -0,0 +1,240 @@ +#include + +static const uint32_t sha256_k[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +#define rotr(v, b) (((uint32_t)v >> b) | (v << (32 - b))) + +static inline void sha256round(uint32_t *hs, uint32_t *w) { + for (int i = 16; i < 64; ++i) { + uint32_t s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3); + uint32_t s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10); + w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0; + } + + uint32_t a = hs[0]; + uint32_t b = hs[1]; + uint32_t c = hs[2]; + uint32_t d = hs[3]; + uint32_t e = hs[4]; + uint32_t f = hs[5]; + uint32_t g = hs[6]; + uint32_t h = hs[7]; + + for (int i = 0; i < 64; ++i) { + uint32_t s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); + uint32_t ch = (e & f) ^ (~e & g); + uint32_t temp1 = (h + s1 + ch + sha256_k[i] + w[i]); + uint32_t s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); + uint32_t maj = (a & b) ^ (a & c) ^ (b & c); + uint32_t temp2 = (s0 + maj); + + h = g; + g = f; + f = e; + e = (d + temp1); + d = c; + c = b; + b = a; + a = (temp1 + temp2); + } + + hs[0] += a; + hs[1] += b; + hs[2] += c; + hs[3] += d; + hs[4] += e; + hs[5] += f; + hs[6] += g; + hs[7] += h; +} + +#define INLINE __attribute__((always_inline)) static inline + +INLINE void sha256block(uint8_t *buf, uint32_t len, uint32_t *dst) { + uint32_t hs[] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + + uint32_t w[64]; + + for (uint32_t i = 0; i < len; i += 64) { + for (uint32_t j = 0; j < 16; j++) { + uint32_t off = (j << 2) + i; + w[j] = (buf[off] << 24) | (buf[off + 1] << 16) | (buf[off + 2] << 8) | + buf[off + 3]; + } + sha256round(hs, w); + } + + dst[0] = hs[0]; + dst[1] = hs[1]; +} + +#define POLYNOMIAL 0xEDB88320 + +INLINE void makeCRC32tab(uint32_t *table) { + for (uint32_t b = 0; b < 256; ++b) { + uint32_t r = b; + for (uint32_t j = 0; j < 8; ++j) { + if (r & 1) + r = (r >> 1) ^ POLYNOMIAL; + else + r = (r >> 1); + } + table[b] = r; + } +} + +INLINE uint32_t crc(const uint8_t *p, uint32_t len, uint32_t *crcTable) { + uint32_t crc = ~0U; + for (uint32_t i = 0; i < len; ++i) + crc = crcTable[*p++ ^ (crc & 0xff)] ^ (crc >> 8); + return (~crc); +} + +INLINE uint32_t murmur3_core(const uint8_t *data, uint32_t len) { + uint32_t h = 0x2F9BE6CC; + const uint32_t *data32 = (const uint32_t *)data; + uint32_t i = len >> 2; + do { + uint32_t k = *data32++; + k *= 0xcc9e2d51; + k = (k << 15) | (k >> 17); + k *= 0x1b873593; + h ^= k; + h = (h << 13) | (h >> 19); + h = (h * 5) + 0xe6546b64; + } while (--i); + return h; +} + +INLINE void murmur3_core_2(const uint8_t *data, uint32_t len, uint32_t *dst) { + // compute two hashes with different seeds in parallel, hopefully reducing + // collisions + uint32_t h0 = 0x2F9BE6CC; + uint32_t h1 = 0x1EC3A6C8; + const uint32_t *data32 = (const uint32_t *)data; + uint32_t i = len >> 2; + do { + uint32_t k = *data32++; + k *= 0xcc9e2d51; + k = (k << 15) | (k >> 17); + k *= 0x1b873593; + + h0 ^= k; + h1 ^= k; + h0 = (h0 << 13) | (h0 >> 19); + h1 = (h1 << 13) | (h1 >> 19); + h0 = (h0 * 5) + 0xe6546b64; + h1 = (h1 * 5) + 0xe6546b64; + } while (--i); + + dst[0] = h0; + dst[1] = h1; +} + +int Reset_Handler(uint32_t *dst, uint8_t *ptr, uint32_t pageSize, + uint32_t numPages) { + uint32_t crcTable[256]; + makeCRC32tab(crcTable); + + for (uint32_t i = 0; i < numPages; ++i) { +#if 0 + sha256block(ptr, pageSize, dst); +#elif 0 + dst[0] = crc(ptr, pageSize, crcTable); + dst[1] = murmur3_core(ptr, pageSize); +#else + murmur3_core_2(ptr, pageSize, dst); +#endif + dst += 2; + ptr += pageSize; + } +#ifdef __arm__ + __asm__("bkpt 42"); +#endif + return 0; +} + +#if 0 +#define PAGE_SIZE 0x400 +#define SIZE_IN_WORDS (PAGE_SIZE / 4) + +#define setConfig(v) \ + do { \ + NRF_NVMC->CONFIG = v; \ + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) \ + ; \ + } while (0) + +void overwriteFlashPage(uint32_t *to, uint32_t *from) { + int same = 1; + for (int i = 0; i <= (SIZE_IN_WORDS - 1); i++) { + if (to[i] != from[i]) { + same = 0; + break; + } + } + if (same) + return; + + // Turn on flash erase enable and wait until the NVMC is ready: + setConfig(NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos); + + // Erase page: + NRF_NVMC->ERASEPAGE = (uint32_t)to; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + ; + + // Turn off flash erase enable and wait until the NVMC is ready: + setConfig(NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos); + + // Turn on flash write enable and wait until the NVMC is ready: + setConfig(NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos); + + for (int i = 0; i <= (SIZE_IN_WORDS - 1); i++) { + *(to + i) = *(from + i); + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + ; + } + + // Turn off flash write enable and wait until the NVMC is ready: + setConfig(NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos); +} + +#endif + +#ifndef __arm__ +#define PS 1024 +#define NP 10 + +#include +#include + +int main() { + + uint8_t buf[NP * PS]; + uint32_t sums[NP * 2]; + memset(buf, 0, sizeof(buf)); + for (int i = 0; i < PS; ++i) + buf[i] = i; + for (int i = 0; i < PS; ++i) + buf[i + PS] = 108; + Reset_Handler(sums, buf, PS, NP); + for (int i = 0; i < NP; ++i) { + printf("%08x %08x\n", sums[i * 2], sums[i * 2 + 1]); + } + + return 0; +} +#endif diff --git a/pxtarget.json b/pxtarget.json index fd754ef3..4aae702e 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -28,8 +28,7 @@ "driveName": "MICROBIT", "hexMimeType": "application/x-microbit-hex", "openocdScript": "source [find interface/cmsis-dap.cfg]; source [find target/nrf51.cfg]", - "upgrades": [ - { + "upgrades": [{ "type": "package", "map": { "microbit": "core", @@ -194,11 +193,12 @@ "serviceId": "microbit" }, "serial": { - "productFilter": "0x0204", - "vendorFilter": "0x0d28", "nameFilter": "^mbed Serial Port", "log": true, - "chromeExtension": "hjcflblhjoglmjjkecamiegdigfkgeni" + "chromeExtension": "hjcflblhjoglmjjkecamiegdigfkgeni", + "vendorId": "0x0d28", + "productId": "0x0204", + "rawHID": true }, "appTheme": { "accentColor": "#5C005C", @@ -234,8 +234,7 @@ "appStoreID": "1092687276", "mobileSafariDownloadProtocol": "microbithex://?data", "extendEditor": true, - "docMenu": [ - { + "docMenu": [{ "name": "Support", "path": "https://support.microbit.org/" }, @@ -271,8 +270,7 @@ ], "hasReferenceDocs": true, "usbDocs": "/device/usb", - "usbHelp": [ - { + "usbHelp": [{ "name": "connection", "os": "*", "browser": "*", @@ -351,4 +349,4 @@ "editor.background": "#ecf0f1" } } -} +} \ No newline at end of file