Qmk_firmware: SAMD51J18A does not debug nor reset

Created on 30 Aug 2018  ·  8Comments  ·  Source: qmk/qmk_firmware

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.

bug core discussion help wanted question

All 8 comments

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.

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.

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.

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:

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

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.

@DarkMio is anything from this still needing addressed?

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mrceephax picture mrceephax  ·  4Comments

alaibe picture alaibe  ·  4Comments

BenRoe picture BenRoe  ·  3Comments

matz-e picture matz-e  ·  4Comments

fredizzimo picture fredizzimo  ·  4Comments