Files
lk/app/mdebug/fw-m0sub.S
Brian Swetland 6e3edb87de [app][mdebug] version 1.0
- 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)
2021-02-01 17:46:33 -08:00

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