- protocol now correctly reports SWO and SWCLK clock rates on set - remove some debug printfs that were causing fw hangs (investigate later) - allow setting a 1MHz SWCLK rate - CMD_ATTACH now supports an argument to select one of several special reset or escape sequences: ATTACH_SWD_RESET: 60 HI, 4 LO, basic line reset ATTACH_JTAG_SWD: escape sequence for JTAG -> SWD mode switch ATTACH_DORMANT_TO_SWD: escape squence to exit dormant mode ATTACH_SWD_TO_DORMANT: and go back again - add special support for writing to DP TARGETSEL register (response phase must be ignored), needed for SWD multidrop support - bump protocol version to 1.3 - bump firmware version to 1.0 (it's long overdue)
614 lines
9.7 KiB
ArmAsm
614 lines
9.7 KiB
ArmAsm
/* fw-m0sub.S
|
|
*
|
|
* Copyright 2015 Brian Swetland <swetland@frotz.net>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
.syntax unified
|
|
|
|
m0_vectors:
|
|
.word 0x18003FF0
|
|
.word m0_reset + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
.word m0_fault + 1
|
|
// external IRQs
|
|
.word m0_fault + 1
|
|
.word m0_irq + 1
|
|
|
|
m0_fault:
|
|
ldr r0, =0x18000000
|
|
ldr r1, =0xeeee0000
|
|
mrs r2, xpsr
|
|
movs r3, #0xFF
|
|
ands r2, r2, r3
|
|
orrs r1, r1, r2
|
|
str r1, [r0]
|
|
b .
|
|
|
|
.ltorg
|
|
|
|
#define REPORT_DELAY 0
|
|
|
|
#define COMM_BASE 0x18004000
|
|
|
|
#define COMM_CMD 0
|
|
#define COMM_ARG0 4
|
|
#define COMM_ARG1 8
|
|
#define COMM_RESP 12
|
|
#define COMM_RETRY 16
|
|
|
|
|
|
#define M4_TXEV 0x40043130 // write 0 to clear
|
|
|
|
#define SGPIO_BASE (0x40101210)
|
|
#define OFF_IN 0
|
|
#define OFF_OUT 4
|
|
#define OFF_OEN 8
|
|
#define SGPIO_IN (0x40101210)
|
|
#define SGPIO_OUT (0x40101214)
|
|
#define SGPIO_OEN (0x40101218)
|
|
|
|
#define CLK_BIT 11
|
|
#define DIO_BIT 14
|
|
#define TEN_BIT 15
|
|
#define CLK_MSK (1 << CLK_BIT)
|
|
#define DIO_MSK (1 << DIO_BIT)
|
|
#define TEN_MSK (1 << TEN_BIT)
|
|
|
|
#define CLK1_OUT (CLK_MSK | TEN_MSK)
|
|
#define CLK0_OUT (TEN_MSK)
|
|
#define CLK1_IN (CLK_MSK)
|
|
#define CLK0_IN (0)
|
|
|
|
#define OEN_IN ((1 << CLK_BIT) | (1 << TEN_BIT))
|
|
#define OEN_OUT ((1 << CLK_BIT) | (1 << DIO_BIT) | (1 << TEN_BIT))
|
|
|
|
#define NOP4 nop ; nop ; nop ; nop
|
|
#define NOP8 NOP4 ; NOP4
|
|
#define NOP16 NOP8 ; NOP8
|
|
|
|
//#define DELAY nop ; nop
|
|
//#define DELAY NOP8
|
|
|
|
// r11 CLK1_OUT const
|
|
// r10 CLK0_OUT const
|
|
// r9 delay subroutine
|
|
// r8 comm_base addr
|
|
// r7 SGPIO_BASE addr
|
|
// r6 DIO_MSK const
|
|
// r5 CLK1_IN const
|
|
// r4 CLK0_IN const
|
|
// r3 outbits data
|
|
|
|
snooze_1m:
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
snooze_2m:
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
snooze_3m:
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
snooze_4m:
|
|
nop ; nop ; nop ; nop
|
|
nop ; nop ; nop ; nop
|
|
snooze_6m:
|
|
nop ; nop ; nop ; nop
|
|
snooze_8m:
|
|
bx lr
|
|
|
|
// delay 0 nops 16MHz
|
|
// delay 2 nops 12MHz
|
|
// delay 4 nops 9.6MHz
|
|
#define DELAY blx r9
|
|
|
|
// 12 cycles + DELAY x 2
|
|
.macro ONE_BIT_OUT
|
|
lsls r2, r3, #DIO_BIT // shift bit 1 to posn
|
|
ands r2, r2, r6 // isolate bit 1
|
|
movs r1, r2 // save bit 1
|
|
add r2, r2, r10 // combine with CLK1
|
|
DELAY
|
|
str r2, [r7, #OFF_OUT] // commit negative edge
|
|
lsrs r3, r3, #1 // advance to next bit
|
|
add r1, r1, r11 // combine with CLK1
|
|
nop
|
|
nop
|
|
DELAY
|
|
str r1, [r7, #OFF_OUT] // commit positive edge
|
|
.endm
|
|
|
|
.macro ONE_BIT_IN
|
|
ands r0, r0, r6 // isolate input bit
|
|
lsls r0, r0, #(31-DIO_BIT) // move to posn 31
|
|
lsrs r3, r3, #1 // make room
|
|
orrs r3, r3, r0 // add bit
|
|
DELAY
|
|
str r4, [r7, #OFF_OUT] // commit negative edge
|
|
ldr r0, [r7, #OFF_IN] // sample input
|
|
nop
|
|
nop
|
|
DELAY
|
|
str r5, [r7, #OFF_OUT] // commit positive edge
|
|
.endm
|
|
|
|
// used for the final parity and turn bits on input so this
|
|
// actually only reads one bit
|
|
read_2:
|
|
push {lr}
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
DELAY
|
|
str r4, [r7, #OFF_OUT]
|
|
ldr r0, [r7, #OFF_IN]
|
|
nop
|
|
nop
|
|
DELAY
|
|
str r5, [r7, #OFF_OUT]
|
|
ands r0, r0, r6 // isolate bit
|
|
lsrs r0, r0, #DIO_BIT // shift to bit0
|
|
nop
|
|
nop
|
|
DELAY
|
|
str r4, [r7, #OFF_OUT]
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
DELAY
|
|
str r5, [r7, #OFF_OUT]
|
|
pop {pc}
|
|
|
|
// w0: <15> <parity:1> <cmd:16>
|
|
// w1: <data:32>
|
|
|
|
|
|
write_16:
|
|
push {lr}
|
|
b _write_16
|
|
write_32:
|
|
push {lr}
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
_write_16:
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
ONE_BIT_OUT
|
|
pop {pc}
|
|
write_1:
|
|
push {lr}
|
|
ONE_BIT_OUT
|
|
pop {pc}
|
|
|
|
read_4:
|
|
push {lr}
|
|
b _read_4
|
|
read_5:
|
|
push {lr}
|
|
b _read_5
|
|
read_32:
|
|
push {lr}
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
_read_5:
|
|
ONE_BIT_IN
|
|
_read_4:
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ONE_BIT_IN
|
|
ands r0, r0, r6 // isolate input bit
|
|
lsls r0, r0, #(31-DIO_BIT) // move to posn 31
|
|
lsrs r3, r3, #1 // make room
|
|
orrs r3, r3, r0 // add bit
|
|
pop {pc}
|
|
|
|
init:
|
|
ldr r0, =CLK1_OUT
|
|
mov r11, r0
|
|
ldr r0, =CLK0_OUT
|
|
mov r10, r0
|
|
ldr r0, =(snooze_4m + 1)
|
|
ldr r0, =(snooze_1m + 1)
|
|
mov r9, r0
|
|
ldr r0, =COMM_BASE
|
|
mov r8, r0
|
|
ldr r7, =SGPIO_BASE
|
|
ldr r6, =DIO_MSK
|
|
ldr r5, =CLK1_IN
|
|
ldr r4, =CLK0_IN
|
|
bx lr
|
|
|
|
#define MAX_RETRY 8192
|
|
|
|
err_fail:
|
|
movs r0, #3
|
|
mov r3, r8
|
|
str r0, [r3, #COMM_RESP];
|
|
pop {pc}
|
|
|
|
err_timeout:
|
|
movs r0, #2
|
|
mov r3, r8
|
|
str r0, [r3, #COMM_RESP];
|
|
pop {pc}
|
|
|
|
cmd_read_txn:
|
|
push {lr}
|
|
|
|
ldr r0, =MAX_RETRY
|
|
//movs r0, #MAX_RETRY
|
|
mov r12, r0
|
|
|
|
rd_retry:
|
|
ldr r3, [r3, #COMM_ARG0]
|
|
bl write_16
|
|
|
|
ldr r3, =OEN_IN
|
|
str r3, [r7, #OFF_OEN]
|
|
bl read_4
|
|
|
|
lsrs r3, r3, #29
|
|
cmp r3, #1 // OK
|
|
beq rd_okay
|
|
|
|
ldr r1, =OEN_OUT
|
|
str r1, [r7, #OFF_OEN]
|
|
|
|
cmp r3, #2 // WAIT
|
|
bne err_fail
|
|
|
|
mov r0, r12
|
|
subs r0, r0, #1
|
|
mov r12, r0
|
|
beq err_timeout
|
|
mov r3, r8
|
|
b rd_retry
|
|
|
|
rd_okay:
|
|
bl read_32
|
|
bl read_2
|
|
ldr r1, =OEN_OUT
|
|
str r1, [r7, #OFF_OEN]
|
|
mov r1, r11
|
|
orrs r1, r1, r6
|
|
str r1, [r7, #OFF_OUT]
|
|
|
|
mov r1, r8 // get COMM_BASE
|
|
str r3, [r1, #COMM_ARG0]
|
|
str r0, [r1, #COMM_ARG1]
|
|
movs r0, #0
|
|
str r0, [r1, #COMM_RESP]
|
|
#if REPORT_DELAY
|
|
mov r0, r12
|
|
str r0, [r1, #COMM_RETRY]
|
|
#endif
|
|
pop {pc}
|
|
|
|
|
|
cmd_write_txn:
|
|
push {lr}
|
|
|
|
ldr r0, =MAX_RETRY
|
|
mov r12, r0
|
|
|
|
wr_retry:
|
|
ldr r3, [r3, #COMM_ARG0]
|
|
bl write_16
|
|
push {r3} // stash parity bit
|
|
|
|
ldr r3, =OEN_IN
|
|
str r3, [r7, #OFF_OEN]
|
|
bl read_4
|
|
|
|
lsrs r3, r3, #29
|
|
cmp r3, #1 // OK
|
|
beq wr_okay
|
|
|
|
pop {r0} // discard saved parity bit
|
|
|
|
ldr r1, =OEN_OUT
|
|
str r1, [r7, #OFF_OEN]
|
|
|
|
cmp r3, #2 // WAIT
|
|
bne err_fail
|
|
|
|
mov r0, r12
|
|
subs r0, r0, #1
|
|
mov r12, r0
|
|
beq err_timeout
|
|
|
|
mov r3, r8
|
|
b wr_retry
|
|
|
|
wr_okay:
|
|
ldr r3, =OEN_OUT
|
|
str r3, [r7, #OFF_OEN]
|
|
bl write_1
|
|
|
|
mov r3, r8
|
|
ldr r3, [r3, #COMM_ARG1]
|
|
bl write_32
|
|
|
|
pop {r3} // recover parity bit
|
|
bl write_1
|
|
|
|
mov r3, r8 // get COMM_BASE
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
#if REPORT_DELAY
|
|
mov r0, r12
|
|
str r0, [r3, #COMM_RETRY]
|
|
#endif
|
|
pop {pc}
|
|
|
|
// write without caring about the response
|
|
cmd_write_blind:
|
|
push {lr}
|
|
|
|
ldr r3, [r3, #COMM_ARG0]
|
|
bl write_16
|
|
push {r3} // stash parity bit
|
|
|
|
ldr r3, =OEN_IN // ignore Trn/Rsp/Trn
|
|
str r3, [r7, #OFF_OEN]
|
|
bl read_5
|
|
|
|
ldr r3, =OEN_OUT
|
|
str r3, [r7, #OFF_OEN]
|
|
|
|
mov r3, r8
|
|
ldr r3, [r3, #COMM_ARG1]
|
|
bl write_32
|
|
|
|
pop {r3} // recover parity bit
|
|
bl write_1
|
|
|
|
mov r3, r8 // get COMM_BASE
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
#if REPORT_DELAY
|
|
mov r0, =MAX_RETRY
|
|
str r0, [r3, #COMM_RETRY]
|
|
#endif
|
|
pop {pc}
|
|
|
|
|
|
cmd_jtag_to_swd:
|
|
push {lr}
|
|
ldr r3, =0xffffffff
|
|
bl write_32
|
|
ldr r3, =0xffffffff
|
|
bl write_32
|
|
ldr r3, =0b1110011110011110
|
|
bl write_16
|
|
|
|
mov r3, r8
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
pop {pc}
|
|
|
|
cmd_swd_to_dormant:
|
|
push {lr}
|
|
ldr r3, =0xffffffff
|
|
bl write_32
|
|
ldr r3, =0xffffffff
|
|
bl write_32
|
|
ldr r3, =0xE3BC
|
|
bl write_16
|
|
|
|
mov r3, r8
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
pop {pc}
|
|
|
|
cmd_dormant_to_swd:
|
|
push {lr}
|
|
// at least 8 HI
|
|
ldr r3, =0xffff
|
|
bl write_16
|
|
// activation sequence (128bit)
|
|
ldr r3, =0x6209F392
|
|
bl write_32
|
|
ldr r3, =0x86852D95
|
|
bl write_32
|
|
ldr r3, =0xE3DDAFE9
|
|
bl write_32
|
|
ldr r3, =0x19BC0EA2
|
|
bl write_32
|
|
// 4 LO, selection sequence, 4 HI
|
|
ldr r3, =0xF1A0
|
|
bl write_16
|
|
|
|
mov r3, r8
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
pop {pc}
|
|
|
|
cmd_reset:
|
|
push {lr}
|
|
// 50+ HI (60 here), 2+ LO (4 here)
|
|
ldr r3, =0xffffffff
|
|
bl write_32
|
|
ldr r3, =0x0fffffff
|
|
bl write_32
|
|
|
|
mov r3, r8
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
pop {pc}
|
|
|
|
|
|
m0_irq:
|
|
push {lr}
|
|
|
|
// clear event from m4
|
|
ldr r0, =M4_TXEV
|
|
movs r1, #0
|
|
str r1, [r0]
|
|
|
|
mov r3, r8 // get COMM_BASE
|
|
ldr r0, [r3, #COMM_CMD]
|
|
cmp r0, #9
|
|
bls good_cmd
|
|
movs r0, #0
|
|
good_cmd:
|
|
lsls r0, r0, #2
|
|
adr r1, cmd_table
|
|
ldr r2, [r1, r0]
|
|
blx r2
|
|
|
|
pop {pc}
|
|
|
|
.align 4
|
|
cmd_table:
|
|
.word cmd_invalid + 1
|
|
.word cmd_nop + 1
|
|
.word cmd_read_txn + 1
|
|
.word cmd_write_txn + 1
|
|
.word cmd_reset + 1
|
|
.word cmd_setclock + 1
|
|
.word cmd_write_blind + 1
|
|
.word cmd_jtag_to_swd + 1
|
|
.word cmd_dormant_to_swd + 1
|
|
.word cmd_swd_to_dormant + 1
|
|
|
|
cmd_invalid:
|
|
movs r0, #9
|
|
str r0, [r3, #COMM_RESP]
|
|
bx lr
|
|
|
|
cmd_nop:
|
|
movs r0, #0
|
|
str r0, [r3, #COMM_RESP]
|
|
bx lr
|
|
|
|
cmd_setclock:
|
|
ldr r0, [r3, #COMM_ARG0]
|
|
cmp r0, #8
|
|
bls good_clock
|
|
movs r0, #0
|
|
good_clock:
|
|
lsls r2, r0, #2
|
|
adr r1, snooze_table
|
|
ldr r1, [r1, r2]
|
|
mov r9, r1
|
|
|
|
// return actual clock used
|
|
str r0, [r3, #COMM_RESP]
|
|
bx lr
|
|
|
|
.align 4
|
|
snooze_table:
|
|
.word snooze_1m + 1
|
|
.word snooze_1m + 1
|
|
.word snooze_2m + 1
|
|
.word snooze_3m + 1
|
|
.word snooze_4m + 1
|
|
.word snooze_4m + 1
|
|
.word snooze_6m + 1
|
|
.word snooze_6m + 1
|
|
.word snooze_8m + 1
|
|
|
|
m0_reset:
|
|
ldr r0, =0x18000000
|
|
ldr r1, =0xaaaa0000
|
|
str r1, [r0]
|
|
|
|
bl init
|
|
|
|
// enable IRQ1 (Event From M4)
|
|
ldr r0, =0xE000E100
|
|
movs r1, #2
|
|
str r1, [r0]
|
|
|
|
m0_idle:
|
|
wfi
|
|
b m0_idle
|