DIY Nitrokey 3A

Hello friends, I just started a personal project of attempting to create my own nk3am. After I realized the code was open source, I purchased a nrf52840 dk and started creating breakout boards for the SE050 and memory module.
My question is this - is there a guide on how to go from new hardware (fresh nrf52840 from the factory) to a functional system? I fumbled around in the makefiles quite a bit, and was able to get to the point of loading a bootloader, and seemed like I was able to also load provisioner firmware, but after the provisioner loads, the USB never seems to come back online (makefile calls wait_for_usb.sh but it never comes back). My current setup only consists of a nrf52840 dk with no SE050 or memory module connected. Is the provisioner firmware looking for hardware in the loop and halts as it isn’t found?
Basically just looking to understand if I’m going in the right direction - the SE050 and memory module should be assembled in the next week or so.
Any help is appreciated!

Any ideas for this? I have the SE050 as well as the memory module connected in the exact same fashion as the NK3am, and am still hitting the same problem - after the provisioner pushes to the device it never comes back online (udev rules are in place) - the wait for usb script eventually fails out due to no device being found (don’t see any activity in udevadm monitor when I disconnect / reconnect the device).

If a call works, let me know and I can set something up.

you should contact support directly by email or join matrix chat for quicker response

1 Like

Hey thanks for the response. I actually started this by sending them an email but I was directed here. Also, could you help me understand what the matrix chat is? I haven’t heard of it

its a chat messenger

Thank you for that - I registered and posted to the chat.

hey hey

yeah the issue you observe is mostly to be expected. Please keep in mind that the nrf-dk is indeed the same MCU, but neither pin-compatible nor equipped with all the peripherals.

To continue your project I’d suggest to:

  1. get to know and use the debugger (+required host tooling which should all be available through utils/nrf-debugging), there is most likely a panic you encounter during boot, you will have a pretty hard time to guess this w/o any outputs
  2. se050 and ext-flash should not lead to boot panics, they’ll just appear as ā€œerrorsā€ within nitropy nk3 status
  3. most likely you’ll need to adapt the pin mappings (nitrokey-3-firmware/components/boards/src/nk3am.rs at main Ā· Nitrokey/nitrokey-3-firmware Ā· GitHub)

but yes, most importantly you need some output path (debugger) so you can read panic messages anything else is guessing.

edit: at some point there was a board-config in the repo some year(s) back, but I am not sure this will help a lot - still you could try

1 Like

Hey daringer, thank you very much for that response. I ran through things and gathered logs, and see two faults.

One is the flash-develop command tries to reboot into bootloader mode and it complains it can only boot bootloader from firmware mode (it then proceeds to upload the develop firmware), below is the output.

The other is once the firmware is loaded, the rtt log shows app: No firmware to activate. rtt log pasted below the flash-develop log. Is there something that needs to happen for the initial firmware load?

I checked the board pin mappings and they look good (memory / se050 / touch sensor connected to correct pins)

Again thank you very much for your time here, I really do appreciate it.

flash-develop log:

  • nrfutil pkg generate --hw-version 52 --application-version 4194818 --application develop-nk3am-nrf52-1.8.2.hex --sd-req 0x0 --key-file …/test-certificates/firmware-nk3am/dfu_private.key --app-boot-validation VALIDATE_ECDSA_P256_SHA256 develop-nk3am-nrf52-1.8.2.zip
    Zip created at develop-nk3am-nrf52-1.8.2.zip
    nitropy nk3 reboot --bootloader
    Command line tool to interact with Nitrokey devices 0.8.1
    Critical error:
    A device in bootloader mode can only reboot into firmware mode.
    make: [Makefile:95: flash-develop] Error 1 (ignored)
    bash wait_for_usb.sh 20a0:42e8
    Bus 001 Device 034: ID 20a0:42e8 Clay Logic Nitrokey 3 Bootloader
    ā€œā€¦/ā€¦ā€/utils/nrf-bootloader/upload.sh develop-nk3am-nrf52-1.8.2.zip /dev/serial/by-id/usb-Nitrokey_Nitrokey_3_Bootloader*
  • ā€˜[’ 2 -ne 2 ā€˜]’
  • UPDATE_FILENAME=develop-nk3am-nrf52-1.8.2.zip
  • SERIAL_PORT=/dev/serial/by-id/usb-Nitrokey_Nitrokey_3_Bootloader_C4711DE56354-if00
  • nrfutil dfu usb-serial -pkg develop-nk3am-nrf52-1.8.2.zip -p /dev/serial/by-id/usb-Nitrokey_Nitrokey_3_Bootloader_C4711DE56354-if00
    [####################################] 100%
    Device programmed.
    bash wait_for_usb.sh 20a0:42b2
    …Device not found
    make: *** [Makefile:100: flash-develop] Error 1

rtt log shows each time the chip reboots after firmware flash:

app: In nrf_bootloader_init

nrf_dfu_settings: Calling nrf_dfu_settings_init()…

nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.

nrf_dfu_settings: Using settings page.

nrf_dfu_settings: Copying forbidden parts from backup page.

nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.

nrf_dfu_settings: Backing up settings page to address 0xFE000.

nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.

app: Enter nrf_bootloader_fw_activate

app: No firmware to activate.

nrf_dfu_validation: Signature required. Checking signature.

nrf_dfu_validation: Calculating hash (len: 536472)

nrf_dfu_validation: Verify signature

nrf_dfu_validation: Image verified

app: App is valid

nrf_dfu_settings: No additional data erased

nrf_dfu_settings: Backing up settings page to address 0xFE000.

nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.

app: Running nrf_bootloader_app_start with address: 0x00001000

app: Disabling interrupts. NVIC->ICER[0]: 0x0

make: *** [Makefile:31: rtt] Interrupt

1 Like

ok nice! This is good progress - this looks all pretty good, can you please try to run: make dev-deploy this should upload firmwares twice (first the provisioner and then the final-firmware).

It might also make sense to go back some steps and try some simple example on the nrf-dk (blink-led) to ensure that the nrf is working in general.

Hey man - went in and wiped the device with nrfutil, and then flashed it with the blinky code - worked just fine (leds flashed as expected, and was able to modify flash rate and re-upload code). After doing this, I wiped the mcu again and executed make dev-deploy. Unfortunately it was the same result as previously. The bootloader executed and loaded (I can see the bootloader with lsusb before the fw loads), but then the provisioner loads, sends the provisioner fw image to the 52840, it shows ā€˜Device Programmed’, then the wait_for_usb.sh script executes and the device never comes back. At this point I don’t even see the bootloader in lsusb

Here is the final output from make dev-deploy:

Zip created at provisioner-nk3am-nrf52-1.8.2.zip
nitropy nk3 reboot --bootloader
Command line tool to interact with Nitrokey devices 0.8.1
Critical error:
A device in bootloader mode can only reboot into firmware mode.
make[1]: [Makefile:103: flash-provisioner] Error 1 (ignored)
bash wait_for_usb.sh 20a0:42e8
Bus 001 Device 016: ID 20a0:42e8 Clay Logic Nitrokey 3 Bootloader
"../.."/utils/nrf-bootloader/upload.sh provisioner-nk3am-nrf52-1.8.2.zip /dev/serial/by-id/usb-Nitrokey_Nitrokey_3_Bootloader*
+ '[' 2 -ne 2 ']'
+ UPDATE_FILENAME=provisioner-nk3am-nrf52-1.8.2.zip
+ SERIAL_PORT=/dev/serial/by-id/usb-Nitrokey_Nitrokey_3_Bootloader_C4711DE56354-if00
+ nrfutil dfu usb-serial -pkg provisioner-nk3am-nrf52-1.8.2.zip -p /dev/serial/by-id/usb-Nitrokey_Nitrokey_3_Bootloader_C4711DE56354-if00
  [####################################]  100%
Device programmed.
bash wait_for_usb.sh 20a0:42b2
..............................Device not found
make[1]: *** [Makefile:108: flash-provisioner] Error 1
make[1]: Leaving directory '/home/BLIP/code/nitrokey-3-firmware/utils/nrf-builder'
make: *** [Makefile:127: dev-deploy] Error 2

Below is the output from rtt (shows the same error about no firmware loaded).

<debug> app: In nrf_bootloader_init
<debug> nrf_dfu_settings: Calling nrf_dfu_settings_init()...
<debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
<debug> nrf_dfu_settings: Using settings page.
<debug> nrf_dfu_settings: Copying forbidden parts from backup page.
<debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
<info> nrf_dfu_settings: Backing up settings page to address 0xFE000.
<debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
<debug> app: Enter nrf_bootloader_fw_activate
<info> app: No firmware to activate.
<info> nrf_dfu_validation: Signature required. Checking signature.
<info> nrf_dfu_validation: Calculating hash (len: 533776)
<info> nrf_dfu_validation: Verify signature
<info> nrf_dfu_validation: Image verified
<debug> app: App is valid
<warning> nrf_dfu_settings: No additional data erased
<info> nrf_dfu_settings: Backing up settings page to address 0xFE000.
<debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
<debug> app: Running nrf_bootloader_app_start with address: 0x00001000
<debug> app: Disabling interrupts. NVIC->ICER[0]: 0x0

I’m not sure where to go next to be honest. Thank you again for the help

One other thing - I’m not sure if it is a GDB thing or what, but when I run make it (with ocd up and running), I hit this error. I associated that with me starting gdb with the program running flags a panic… am I wrong? Here is the output:

make it
arm-none-eabi-gdb -x gdb.cmds
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
The target architecture is set to "arm".
The target is set to little endian.
boards::handle_panic<boards::nk3am::NK3AM> (
    _info=<optimized out>)
    at components/boards/src/lib.rs:159
159             core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
(gdb) c
Continuing.

the rtt output still looks ā€œokā€ it’s mostly the bootloader.

Keep in mind that there always will only be one of the two, means: either the bootloader is running (then you see its pid/vid in lsusb) or the firmware is running, then you should see the nk3 vid:pid.

So once the firmware upload is complete the firmware should start and the bootloader will vanish. BUT your gdb output implies a panic, as it is stuck here.

If I remember correctly rtt has problems reconnecting to the firmware fast enough if during boot a panic occurs, now we would need to know were this panic happens - so we’d need a backtrace from within gdb … just a bt instead of c should do it.

Furthermore the fact that gdb gives us this information means that the firmware seems to start, but fails very early (I don’t see how otherwise this useful source location output should appear)

edit: I forgot that you need to run make dev-deploy EXTRA_FEATURES=log-rtt,log-all FEATURES=log-rtt,log-all in order to also get rtt outputs from the firmware, you should then also see the panic message in the firmware output (and generally more hints where it fails) …

Now we are getting somewhere! Thank you again.

Here is the output from rtt:

nc localhost 4999
Reset Reason: 100
Reset Reason: ResetReason { resetpin: false, dog: false, sreq: true, lockup: false, off: false, lpcomp: false, dif: false, nfc: false, vbus: false }
Unknown Ext. Flash: Identification([c0, 00, 18])
panicked at components/boards/src/nk3am.rs:216:40:
called `Option::unwrap()` on a `None` value
panicked at components/boards/src/nk3am.rs:216:40:
called `Option::unwrap()` on a `None` value

Line 216 is the ExtFlashStorage::try_new(spim, cs).unwrap() line of the function below

pub fn init_external_flash(spim3: SPIM3, spi: spim::Pins, cs: OutPin) -> ExternalFlashStorage {
    let spim = Spim::new(spim3, spi, spim::Frequency::M2, spim::MODE_0, 0x00u8);
    ExtFlashStorage::try_new(spim, cs).unwrap()
}

And here is the more verbose GDB log with backtrace:

The target is set to little endian.
boards::handle_panic<boards::nk3am::NK3AM> (_info=0x2001ed10)
    at components/boards/src/lib.rs:159
159             core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
(gdb) bt
#0  boards::handle_panic<boards::nk3am::NK3AM> (_info=0x2001ed10)
    at components/boards/src/lib.rs:159
#1  0x0004d65c in nrf52_runner::panic (info=0x3)
    at runners/embedded/src/bin/app-nrf.rs:296
#2  0x00055876 in core::panicking::panic_fmt () at core/src/panicking.rs:74
#3  0x00056116 in core::panicking::panic () at core/src/panicking.rs:148
#4  0x00056eb4 in core::option::unwrap_failed () at core/src/option.rs:2015
#5  0x00050c82 in core::option::Option<boards::flash::ExtFlashStorage<nrf_hal_common::spim::Spim<nrf52840_pac::SPIM3>, nrf_hal_common::gpio::Pin<nrf_hal_common::gpio::Output<nrf_hal_common::gpio::PushPull>>>>::unwrap<boards::flash::ExtFlashStorage<nrf_hal_common::spim::Spim<nrf52840_pac::SPIM3>, nrf_hal_common::gpio::Pin<nrf_hal_common::gpio::Output<nrf_hal_common::gpio::PushPull>>>> (self=...)
    at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/option.rs:965
#6  boards::nk3am::init_external_flash (spi=..., cs=...)
    at components/boards/src/nk3am.rs:216
#7  nrf52_runner::app::init (ctx=...)
    at runners/embedded/src/bin/app-nrf.rs:88
#8  nrf52_runner::app::rtic_ext::main::{closure#3} ()
    at runners/embedded/src/bin/app-nrf.rs:8
#9  nrf52_runner::app::rtic_ext::main::__rtic_init_resources<nrf52_runner::app::rtic_ext::main::{closure_env#3}> (f=...)
    at runners/embedded/src/bin/app-nrf.rs:8
#10 0x0004e2e6 in nrf52_runner::app::rtic_ext::main ()
    at runners/embedded/src/bin/app-nrf.rs:8

Is this an external flash issue?

yes, this is quite surely a communication issue with the ext-flash (through SPI) - I have incorrectly remembered that this should be a non-panic case, but this is only true for the lpc variant. I think from here you have various possibilities to progress:

  • ensure that the ext-flash is working properly and connected properly
  • skip ext-memory for now, this is not necessarily trivial, I see different possibilities:

Generally from experience I’d suggest to first skip as many peripherals as possible and try to have a running firmware first, from there it will be easier to get the peripherals up and running. Means you could run into similar issues with the se050, although the firmware already comes with a feature se050 to enable/disable it.

1 Like

Okay well I am on a mission to figure this one out now. I connected a logic analyzer and am seeing some unique results…

Here is the SPI traffic between the memory unit and a functional system (used an arduino to check it out). All I am running here is a command to pull the systems signature (sending 0x9F) - expecting a MISO response as 0xC8 0x40 0x15, which this shows. On this system, I can read memory, write, etc no problem.

Here is the SPI traffic between the memory unit and the 52840 DK… This is running the Nitro Firmware, and looking at the code the first thing it does is request the memory signature (expecting the same values 0xC8 0x40 0x15), but when the 0x9F is sent to the memory, it responds back with 0x00 0x80 0x00 0x30, not sure what is going on to be totally honest. They are connected identically, but they are acting differently. My only guess is there is something on the 52840 DK board that is interfering with the response. While I wait for a custom board to arrive, I’m going to get a 52840 dongle to do some additional testing on, as this looks like a more barebones system. We’ll see if this is any different.

Thank you again for all the help.

when there is too much noise, maybe lowering the baud rate would improve communication.

1 Like

Hey friend- i did try bringing the clock rate all the way down to 500 and 250khz, but the output was identical- with that I don’t think it is a noise thing. The wires I use to plug into the 52840 are the same ones I use to connect to the functional system (arduino)

I’ve tried many different spi ports on the 52840 but the outcome is always the same…… something funky is happening here

@daringer I have made an advancement… finally :slight_smile:

After a bit of trial and error I am finally receiving the correct JEDEC information back from the flash memory, good god that took some debugging. Onto the next problem, hoping you can give some insight on things to try next with this. From the store.rs (snippit below), I am now able to get past the internal and external flash initialization, but when I get to the store::init_store() function call I error out.

I can see the nrf52840 and spi memory chatting over the logic analyzer - is there something specific I should look for in that? There is a lot of chatter between the two, so it’d be tough to paste a snippit. I can attach the entire capture if that’d help (Saleae device).

Again I really appreciate all the patience / help.

Code for reference (of where I error out)

pub fn init_store<B: Board>(
    resources: &'static mut StoreResources<B>,
    int_flash: B::InternalStorage,
    ext_flash: B::ExternalStorage,
    simulated_efs: bool,
    status: &mut InitStatus,
) -> RunnerStore<B> {
    let ifs_storage = resources.internal.storage.write(int_flash);
    let ifs_alloc = resources.internal.alloc.write(Filesystem::allocate());
    let efs_storage = resources.external.storage.write(ext_flash);
    let efs_alloc = resources.external.alloc.write(Filesystem::allocate());
    let vfs_storage = resources.volatile.storage.write(VolatileStorage::new());
    let vfs_alloc = resources.volatile.alloc.write(Filesystem::allocate());

    let ifs = match init_ifs::<B>(ifs_storage, ifs_alloc, efs_storage, status) {
        Ok(ifs) => resources.internal.fs.write(ifs),
        Err(_e) => {
            error!("IFS Mount Error {:?}", _e);
            panic!("IFS");
        }
    };

    let efs = match init_efs::<B>(efs_storage, efs_alloc, simulated_efs, status) {
        Ok(efs) => resources.external.fs.write(efs),
        Err(_e) => {
            error!("EFS Mount Error {:?}", _e);
            panic!("EFS");
        }
    };

The error I am now hitting:

Reset Reason: 1
Reset Reason: ResetReason { resetpin: true, dog: false, sreq: false, lockup: false, off: false, lpcomp: false, dif: false, nfc: false, vbus: false }
EFS Mount Error Error
EFS Reformat Err(Error)
panicked at /home/BLIP/code/nitrokey-3-firmware/components/boards/src/store.rs:154:13:
EFS
panicked at /home/BLIP/code/nitrokey-3-firmware/components/boards/src/store.rs:154:13:
EFS

And here is the backtrace:

(gdb) bt
#0  boards::handle_panic<boards::nk3am::NK3AM> (
    _info=0x2001ed54) at components/boards/src/lib.rs:159
#1  0x0004d58c in nrf52_runner::panic (info=0x3)
    at runners/embedded/src/bin/app-nrf.rs:296
#2  0x000557e8 in core::panicking::panic_fmt ()
    at library/core/src/panicking.rs:75
#3  0x00050d54 in boards::store::init_store<boards::nk3am::NK3AM> (resources=<optimized out>, ext_flash=..., 
    simulated_efs=false, int_flash=..., 
    status=<optimized out>)
    at /home/BLIP/.rustup/toolchains/1.86.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:602
#4  nrf52_runner::app::init (ctx=...)
    at runners/embedded/src/bin/app-nrf.rs:93
#5  nrf52_runner::app::rtic_ext::main::{closure#3} ()
    at runners/embedded/src/bin/app-nrf.rs:8
#6  nrf52_runner::app::rtic_ext::main::__rtic_init_resources<nrf52_runner::app::rtic_ext::main::{closure_env#3}> (f=...)
    at runners/embedded/src/bin/app-nrf.rs:8
#7  0x0004e212 in nrf52_runner::app::rtic_ext::main ()
    at runners/embedded/src/bin/app-nrf.rs:8

hey @jaboticaba

I admire your endurance! But well, yes that’s embedded development at its best :wink: Generally this looks better, but my wild guess is that still something is wrong with your SPI comms or the NAND memory itself.

Regarding your issue: it is quite clear that formatting the ext-mem fails, looking deeper into init_efs you can see that mounting fails (well obviously) then the ext-mem should be formatted, but this fails, too. Honestly, this is something I haven’t seen before and is veeeery likely due to some communication issue with the ext-mem…

Can you please elaborate what you had to do in order to get the initial identification working. I am pretty sure (if not 100% sure) that we run exactly this code for the production devices with the external memory, so I am also curios what is the difference for the nrf-dk, because afair we’ve never had a nrf-dk setup together with this memory running, we’ve been using a nk3-prototype pretty early.

btw.: you could also buy a Nitrokey 3 Mini ā€œHackerā€, which would essentially be a NK3 device which is not sealed thus you can simply put your own firmware on it through a SWD debugger (wires are soldered) … Maybe this is the better option for you if you are more interested in the software side of things, if you are more into hardware tinkering then your nrf-dk setup might be just right :smiley:

I appreciate that! Honestly this is all a huge learning opportunity for me, and all of your help is hugely appreciated as well.

The only thing in code that needed to get changed in order for things to work was in the nrf-bootloader makefile, I need to comment out the REGOUT0 to 3v3 line… for whatever reason leaving that on causes the SPI memory to read incorrect 100% of the time. After commenting it out, it reads the JEDEC correct 100% of the time, and does look like it is responding to other commands. Commented code below

# UICR
## UICR::REGOUT0 to 3v3
#nrfjprog -f NRF52 --memwr 0x10001304 --val 0xfffffffd --verify #LINE I COMMENTED

Delayed due to a bit more debugging

At this point I think it is a power thing. After getting out a separate power supply. The only time I can get this thing to work is when I set the power source as USB and connect spi memory power to vdd of the nrf-dk (1.8v out) (JEDEC reads correct). The issue is that the memory is rated from 2.7 to 3.6v… probably explains why it isn’t working quite right. The odd part is when I manually set the gpio voltage to 3v, and I apply an external 3.0v to the memory, I get an incorrect JEDEC ID again (same thing as I saw earlier). What is even MORE odd is when I set the voltage to 3.3v, and update the gpio voltage to 3.3v, the spi memory doesn’t respond at all (MISO stays low the entire time)…

This must be power related. The power setup on the nitrokey is definitely different than the 52840-dk… I have a custom board coming in this week where I mirrored the power configuration as the nk3am… we’ll see if I can solder the odd BGA-like package of this thing.

I’m giving up for the night and going to sleep. Thank you again for the help.