Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAMD51J18A does not debug nor reset #3808

Closed
DarkMio opened this issue Aug 30, 2018 · 8 comments
Closed

SAMD51J18A does not debug nor reset #3808

DarkMio opened this issue Aug 30, 2018 · 8 comments

Comments

@DarkMio
Copy link

DarkMio commented Aug 30, 2018

I got the recently shipped CTRL Massdrop Keyboard, which runs a SAMD51J18A.

While tinkering with it, I have had no luck getting any debug output from it nor getting it to reset.

The configuration:

SRC = led_programs.c
SRC += matrix.c
ARM_ATSAM = SAMD51J18A
MCU = cortex-m4
CUSTOM_MATRIX = yes
EXTRAKEY_ENABLE = yes   # Audio control and System control
CONSOLE_ENABLE = yes    # Console for debug (enables debug prints to virtser, no console support)
NKRO_ENABLE = yes       # USB Nkey Rollover
#VIRTSER_ENABLE = yes    # USB Serial Driver

Other than that, I've written a keymap that does the command but no output. LShift, RShift magic + D didn't seem to do anything either. At this point I'm not even sure if the magic works correctly, the (default) configuration for it looks like that:

#ifndef CONFIG_H
#define CONFIG_H
#define VENDOR_ID           0x04D8
#define PRODUCT_ID          0xEED2
#define DEVICE_VER          0x0101
#define MANUFACTURER        "Massdrop Inc."
#define PRODUCT             "CTRL Keyboard"
#define SERIAL_NUM          "00017"
#define MATRIX_ROWS 11
#define MATRIX_COLS 8
#define PA 0
#define PB 1
#define MATRIX_ROW_PORTS PB, PB, PB, PB, PB, PB, PA, PA, PB, PB, PB
#define MATRIX_ROW_PINS   4,  5,  6,  7,  8,  9, 10, 11, 10, 11, 12
#define MATRIX_COL_PORTS PA, PA, PA, PA, PA, PA, PA, PA
#define MATRIX_COL_PINS   0,  1,  2,  3,  4,  5,  6,  7
#define MCU_HZ 48000000
#define DEBOUNCING_DELAY 5
#define IS_COMMAND() ( \
    keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
)
#define FORCE_NKRO
#endif

This way I'm listening on all HIDs (I believe there are four) coming from this USB controller:

namespace Octopode.CLI {
    class Program {
        private static async void ReadLoop(HidDevice usbDevice) {
            HidDeviceData dataPoint = await usbDevice.ReadAsync();
            while (true) {
                dataPoint = await usbDevice.ReadAsync();
                if (dataPoint.Status != HidDeviceData.ReadStatus.Success) {
                    throw new InvalidOperationException($"Cannot read from USB device! State was {dataPoint.Status}");
                }

                foreach (var bit in dataPoint.Data) {
                    if(bit == 0) {
                        continue;
                    }
                    Console.WriteLine(bit);
                }
            }
        }

        static void Main(string[] args) {
            var devices = new List<HidDevice>(DeviceEnumerator.EnumerateAllDevices().Where(x => x.Attributes.ProductId == 0xEED2 && x.Attributes.VendorId == 0x04D8));
            var tasks = new List<Task>();
            foreach(var device in devices) {
                tasks.Add(Task.Factory.StartNew(() => { ReadLoop(device); }));
            }
            while(true) {
                System.Threading.Thread.Sleep(1000);
            }
        }
    }
}

Any help is highly appreciated.

@drashna
Copy link
Member

drashna commented Aug 30, 2018

Does HID_listen work?
https://www.pjrc.com/teensy/hid_listen.html

As for the reset, that's probably a setting in the reset code that isn't configured properly for the SAMD51J18A chips.

@DarkMio
Copy link
Author

DarkMio commented Aug 30, 2018

I guessed as much. HID_Listen does not work, which is why I used my HID listener just to be sure.

The misconfiguration seems to be caused by this ruleset:

/**
 * \file
 *
 * \brief Linker script for running in internal FLASH on the SAMD51J18A
 *
 * Copyright (c) 2017 Microchip Technology Inc.
 *
 * \asf_license_start
 *
 * \page License
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 Licence 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.
 *
 * \asf_license_stop
 *
 */


OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)

/* Memory Spaces Definitions */
MEMORY
{
  //rom      (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00040000
  rom      (rx)  : ORIGIN = 0x00004000, LENGTH = 0x0003C000
  ram      (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00020000
  bkupram  (rwx) : ORIGIN = 0x47000000, LENGTH = 0x00002000
  qspi     (rwx) : ORIGIN = 0x04000000, LENGTH = 0x01000000
}

/* The stack size used by the application. NOTE: you need to adjust according to your application. */
STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x8000;

_srom = ORIGIN(rom);
_lrom = LENGTH(rom);
_erom = ORIGIN(rom) + LENGTH(rom);

/* Section Definitions */
SECTIONS
{
    .text :
    {
        . = ALIGN(4);
        _sfixed = .;
        KEEP(*(.vectors .vectors.*))
        *(.text .text.* .gnu.linkonce.t.*)
        *(.glue_7t) *(.glue_7)
        *(.rodata .rodata* .gnu.linkonce.r.*)
        *(.ARM.extab* .gnu.linkonce.armextab.*)

        /* Support C constructors, and C destructors in both user code
           and the C library. This also provides support for C++ code. */
        . = ALIGN(4);
        KEEP(*(.init))
        . = ALIGN(4);
        __preinit_array_start = .;
        KEEP (*(.preinit_array))
        __preinit_array_end = .;

        . = ALIGN(4);
        __init_array_start = .;
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        __init_array_end = .;

        . = ALIGN(4);
        KEEP (*crtbegin.o(.ctors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
        KEEP (*(SORT(.ctors.*)))
        KEEP (*crtend.o(.ctors))

        . = ALIGN(4);
        KEEP(*(.fini))

        . = ALIGN(4);
        __fini_array_start = .;
        KEEP (*(.fini_array))
        KEEP (*(SORT(.fini_array.*)))
        __fini_array_end = .;

        KEEP (*crtbegin.o(.dtors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
        KEEP (*(SORT(.dtors.*)))
        KEEP (*crtend.o(.dtors))

        . = ALIGN(4);
        _efixed = .;            /* End of text section */
    } > rom

    /* .ARM.exidx is sorted, so has to go in its own output section.  */
    PROVIDE_HIDDEN (__exidx_start = .);
    .ARM.exidx :
    {
      *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > rom
    PROVIDE_HIDDEN (__exidx_end = .);

    . = ALIGN(4);
    _etext = .;

    .relocate : AT (_etext)
    {
        . = ALIGN(4);
        _srelocate = .;
        *(.ramfunc .ramfunc.*);
        *(.data .data.*);
        . = ALIGN(4);
        _erelocate = .;
    } > ram

    .bkupram (NOLOAD):
    {
        . = ALIGN(8);
        _sbkupram = .;
        *(.bkupram .bkupram.*);
        . = ALIGN(8);
        _ebkupram = .;
    } > bkupram

    .qspi (NOLOAD):
    {
        . = ALIGN(8);
        _sqspi = .;
        *(.qspi .qspi.*);
        . = ALIGN(8);
        _eqspi = .;
    } > qspi

    /* .bss section which is used for uninitialized data */
    .bss (NOLOAD) :
    {
        . = ALIGN(4);
        _sbss = . ;
        _szero = .;
        *(.bss .bss.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = . ;
        _ezero = .;
    } > ram

    /* stack section */
    .stack (NOLOAD):
    {
        . = ALIGN(8);
        _sstack = .;
        . = . + STACK_SIZE;
        . = ALIGN(8);
        _estack = .;
    } > ram

    . = ALIGN(4);
    _end = . ;
}

I can do __NVIC_SystemReset() just fine by now, just the magic byte isn't set, tho. So I'm currently fiddling around, figuring out what ram0_end could be to work correctly.

Edit: Further inspection of the fork indicates that the Massdrop people have added this entire chipset, so it's a bit hard to trace where they failed. Poking into random memory doesn't seem to do anything good either.

@DarkMio
Copy link
Author

DarkMio commented Aug 30, 2018

Here's a fun bit of great software engineering:

https://github.com/Massdrop/qmk_firmware/blob/master/tmk_core/common/arm_atsam/bootloader.c#L19

That's why bootloader_jump() does not work at all.

@DarkMio
Copy link
Author

DarkMio commented Aug 31, 2018

Looking at it after some good rest, I figured out how this thing works on debugging. Generally the firmware implementations ignores going via HID USB reports and uses entirely serial. Looking at the amount of code that massdrop has written themselves, they probably had a good reason for that.

So reading from the device with hid_listen or similiar software wont work. However, all you need is a serial terminal and point it to the right interface, in my case it was screen /dev/cu.usbmodem14224 96000

Baudrate of 96000 was just a good guess and it seems to work just fine. Next time I should just look at the usb descriptor instead of headbutting straight into HID. 🤷‍♂️

Demo:

@DarkMio
Copy link
Author

DarkMio commented Aug 31, 2018

Tagging @patrickmt as he seems to develop the software for the CTRL.

@DarkMio
Copy link
Author

DarkMio commented Sep 1, 2018

Since yesterday we had quite a development. The mdloader does not flash the boot rom for safety reasons. Also there is no bootloader code in this repository, so you have no control about booting. The bootloader basically does the following:

def boot_img():
    long a = read(0x40000, 64)
    if irq_reset or a == 0xFFFFFFFF_FFFFFFFF:
        boot_flasher()
    else:
        boot_rom()

Since you either have to have a broken rom or need to press the button that is wired to the CPU RESET, there is currently no easy way of doing it. Talking with Patrick, he's onto a solution, which involves resetting the CPU to a state which it would be like while booting and then jumping directly into the flashing region. That is, to the best of my knowledge, hardly possible from my side without the bootloader or flashing image (except you can dump from the keyboard).

So we'll wait on a resolution of Massdrops side. This issue can be closed for now.

@patrickmt
Copy link
Contributor

@DarkMio is anything from this still needing addressed?

@DarkMio
Copy link
Author

DarkMio commented Oct 3, 2018

Bootloader jump works fine. The default debugging method still does not work, but that should be simply added with a comment on the readme of that board, that it actually is only willing to talk over serial. Otherwise good work so far.

@DarkMio DarkMio closed this as completed Oct 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants