Originally, I submit the entry of
https://www.hackster.io/gniibe/bbg-swd-93bcea
But I had a trouble to submit the entry above as my submission to the contest.
Thus, I cut&paste things from the original project to make this new entry.
BeagleBoneGreen SWD - a practical tool for computing freedomhttp://git.gniibe.org/gitweb/?p=bbg-swd.git;a=summary
Benefit
It's all-in-one SWD programmer. You can just login to the system and invoke OpenOCD. No installation of any proprietary software on to your PC is required. The implementation is done by Free Software only.
Usefulness
Any applications for "THING"s require programming some sort of generation of signals, PWM, serial communications, whatever. And any devices (with flash) are required to be flashed.
BBG-SWD is a concrete example to control signal accurately, so, it can be a good start point to learn use of PRUSS controlling signal. Having trustworthy technology for flashing helps everyone.
The Problem to Solve
I design FST-01, the USB 32-bit "computer" with STM32F103, so that I can use it for my crypto computation securely (OpenPGP, SSH, etc.). To flash this board, I used ST-Link/V2 or its compatible. But, here is the problem. Am I sure flashing the binary reliably?
How can we reliably flash our boards securely?
Motivation
In modern crypto, we have strong enough algorithms and implementations. The problems tend to be in the platform which runs those crypto computations and environment/process which enables such a computation. That's my viewpoint, and I develop my own solution, named Gnuk Token. For Gnuk Token, I design FST-01.
Then, I encounter the problem above. Before BBG-SWD, I have no concrete way to ask manufacturer how to flash FST-01 reliably.
I wanted to fix missing link and/or possible weakest link to achieve best practical crypto computation.
Writing process of firmware would be a missing link in freedom of user computing
Writing process of firmware for "secure device" would be possible weakest link
How to Fix the Problem
By BBG as all-in-one tool
By using PRUSS to do the Right Thing
Comparison
- ST-Link/V2 with software tool on PC
Firmware in ST-Link/V2 would be weakest link, nobody except vendor can check the correctness. Proprietary tool would be also weak against attacks.
- Bus Pirate with software tool on PC
Even if software tool could be Free Software, Operating System would be weakest link.
- Bus Pirate with BBG
This is the best practice (until BBG-SWD).
- BBG alone, directly connected to the target board, that's BBG-SWD
This is ultimate solution.
HOWTO
(0) BBG with Debian
Kernel should be bone-kernel (not ti) to use uio_pruss module.
https://github.com/beagleboard/am335x_pru_package must be already in your system.
(1) Clone BBG-SWD repository
Get it and build PRU-SWD binary with pasm. The instruction is in the source code (pru-swd.p).
(2) Clone OpenOCD repository http://repo.or.cz/w/openocd.git
Get it and apply a patch in BBG-SWD. Build with configure option of --enable-bbg-swd.
(3) Connect your target board to BBG
See following for pins.
(4) Enjoy OpenOCD with BBG-SWD driver
Future Works
SRST support should be added soon.
If throughput is important, queuing system should be considered (currently, it's simple request and response).
From ee1d60eba7f9c35571241c1da4c8175c8b98daa1 Mon Sep 17 00:00:00 2001
From: NIIBE Yutaka <gniibe@fsij.org>
Date: Tue, 22 Mar 2016 03:06:37 +0000
Subject: [PATCH 1/2] initial patch for BBG-SWD
---
configure.ac | 13 ++
src/jtag/drivers/Makefile.am | 3 +
src/jtag/drivers/bbg-swd.c | 444 +++++++++++++++++++++++++++++++++++++++++++
src/jtag/drivers/bbg-swd.h | 44 +++++
src/jtag/interfaces.c | 6 +
5 files changed, 510 insertions(+)
create mode 100644 src/jtag/drivers/bbg-swd.c
create mode 100644 src/jtag/drivers/bbg-swd.h
diff --git a/configure.ac b/configure.ac
index fb01e1b..d7f4ed9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -527,6 +527,10 @@ AC_ARG_ENABLE([remote-bitbang],
AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang jtag driver]),
[build_remote_bitbang=$enableval], [build_remote_bitbang=no])
+AC_ARG_ENABLE([bbg-swd],
+ AS_HELP_STRING([--enable-bbg-swd], [Enable building support for BBG-SWD.]),
+ [build_bbg_swd=$enableval], [build_bbg_swd=no])
+
AC_MSG_CHECKING([whether to enable dummy minidriver])
if test $build_minidriver_dummy = yes; then
if test $build_minidriver = yes; then
@@ -818,6 +822,14 @@ if test $build_sysfsgpio = yes; then
else
AC_DEFINE([BUILD_SYSFSGPIO], [0], [0 if you don't want SysfsGPIO driver.])
fi
+
+if test $build_bbg_swd = yes; then
+ build_bbg_swd=yes
+ LIBS="$LIBS -lprussdrv"
+ AC_DEFINE([BUILD_BBG_SWD], [1], [1 if you want the BBG-SWD driver.])
+else
+ AC_DEFINE([BUILD_BBG_SWD], [0], [0 if you don't want the BBG-SWD driver.])
+fi
#-- Deal with MingW/Cygwin FTD2XX issues
if test $is_win32 = yes; then
@@ -1230,6 +1242,7 @@ AM_CONDITIONAL([IS_WIN32], [test $is_win32 = yes])
AM_CONDITIONAL([IS_DARWIN], [test $is_darwin = yes])
AM_CONDITIONAL([BITQ], [test $build_bitq = yes])
AM_CONDITIONAL([CMSIS_DAP], [test $use_hidapi = yes])
+AM_CONDITIONAL([BBG_SWD], [test $build_bbg_swd = yes])
AM_CONDITIONAL([MINIDRIVER], [test $build_minidriver = yes])
AM_CONDITIONAL([MINIDRIVER_DUMMY], [test $build_minidriver_dummy = yes])
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 2aaf8fd..4a87324 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -143,6 +143,9 @@ endif
if CMSIS_DAP
DRIVERFILES += cmsis_dap_usb.c
endif
+if BBG_SWD
+DRIVERFILES += bbg-swd.c
+endif
noinst_HEADERS = \
bitbang.h \
diff --git a/src/jtag/drivers/bbg-swd.c b/src/jtag/drivers/bbg-swd.c
new file mode 100644
index 0000000..0363f45
--- /dev/null
+++ b/src/jtag/drivers/bbg-swd.c
@@ -0,0 +1,444 @@
+/***************************************************************************
+ * Copyright (C) 2016 Flying Stone Technology *
+ * Author: NIIBE Yutaka <gniibe@fsij.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <jtag/swd.h>
+
+#include <prussdrv.h>
+#include <pruss_intc_mapping.h>
+
+#define PRU_NUM 0
+
+extern struct jtag_interface *jtag_interface;
+static void bbg_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk);
+static int bbg_swd_switch_seq(enum swd_special_seq seq);
+
+static void pru_request_cmd(uint32_t *p)
+{
+ /* Wakeup the PRU0 which sleeps. */
+ prussdrv_pru_send_event(ARM_PRU0_INTERRUPT);
+
+ /* Wait PRU0 response. */
+ prussdrv_pru_wait_event(PRU_EVTOUT_0);
+ prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
+ if ((p[0] & 0xff) == 4 || (p[0] & 0xff) == 5 || (p[0] & 0xff) == 7)
+ LOG_DEBUG("BBD-SWD: command execution (%08x:%08x)", p[0], p[1]);
+ else
+ LOG_DEBUG("BBD-SWD: command execution (%08x)", p[0]);
+}
+
+static int queued_retval;
+
+#define PRU_SWD_PROGRAM_PATH PKGDATADIR "/bbg-swd/pru-swd.bin"
+
+static uint32_t *pru_data_ram;
+static tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
+
+static int bbg_swd_open(void)
+{
+ int r;
+
+ LOG_DEBUG("bbg_swd_init");
+
+ /* Initialize the PRUSS driver. */
+ prussdrv_init();
+
+ /* Open PRU interrupt to Host. */
+ r = prussdrv_open(PRU_EVTOUT_0);
+ if (r < 0) {
+ LOG_ERROR("prussdrv_open open failed: %d", r);
+ return ERROR_FAIL;
+ }
+
+ /* Initialize PRU interrupt controller. */
+ prussdrv_pruintc_init(&pruss_intc_initdata);
+
+ /* Initialize PRU memory access from Host. */
+ r = prussdrv_map_prumem(PRUSS0_PRU0_DATARAM, (void **)&pru_data_ram);
+ if (r < 0) {
+ prussdrv_exit();
+ LOG_ERROR("prussdrv_map_prumem failed: %d", r);
+ return ERROR_FAIL;
+ }
+
+ /* Execute example on PRU */
+ LOG_DEBUG("Executing PRU-SWU program on PRUSS");
+ r = prussdrv_exec_program(PRU_NUM, PRU_SWD_PROGRAM_PATH);
+ if (r < 0) {
+ prussdrv_exit();
+ LOG_ERROR("prussdrv_exec_program failed: %d", r);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+
+static int bbg_swd_close(void)
+{
+ /* Disable PRU. */
+ prussdrv_pru_disable(PRU_NUM);
+ prussdrv_exit();
+ return ERROR_OK;
+}
+
+
+static int bbg_swd_gpio_srst(int on)
+{
+ /* XXX: not yet implemented */
+ return ERROR_OK;
+}
+
+static bool swd_mode;
+
+static int bbg_swd_interface_init(void)
+{
+ int retval;
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+ if (swd_mode) {
+ retval = bbg_swd_open();
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
+ if (jtag_reset_config & RESET_SRST_NO_GATING) {
+ retval = bbg_swd_gpio_srst(0);
+ if (retval != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_INFO("Connecting under reset");
+ }
+ }
+
+ LOG_INFO("BBG-SWD: Interface ready");
+
+ return ERROR_OK;
+}
+
+static int bbg_swd_interface_quit(void)
+{
+ bbg_swd_close();
+ return ERROR_OK;
+}
+
+static int bbg_swd_swd_init(void)
+{
+ swd_mode = true;
+ return ERROR_OK;
+}
+
+enum {
+ CMD_HALT = 0,
+ CMD_BLINK,
+ CMD_GPIO_OUT,
+ CMD_GPIO_IN,
+ CMD_SIG_IDLE,
+ CMD_SIG_GEN,
+ CMD_READ_REG,
+ CMD_WRITE_REG
+};
+
+#define BBG_SWS_RESULT 16
+
+/*
+ * Signal patterns are defined in:
+ * ARM Debug Interface Architecture Specification (ADI)
+ */
+static const uint8_t seq_jtag_to_swd[] = { 0xde, 0xf9 };
+static const int seq_jtag_to_swd_len = 16;
+static const uint8_t seq_swd_to_seq[] = { 0x3c, 0xe7 };
+static const int seq_swd_to_jtag_len = 16;
+
+static void bbg_swd_idle(int count)
+{
+ pru_data_ram[0] = CMD_SIG_IDLE;
+ pru_data_ram[1] = count;
+ pru_request_cmd(pru_data_ram);
+}
+
+static int bbg_swd_switch_seq(enum swd_special_seq seq)
+{
+ LOG_DEBUG("bbg_swd_switch_seq");
+
+ switch (seq) {
+ case LINE_RESET:
+ LOG_DEBUG("SWD line reset");
+ pru_data_ram[0] = CMD_SIG_GEN | (swd_seq_line_reset_len << 8);
+ memcpy (&pru_data_ram[1], swd_seq_line_reset, swd_seq_line_reset_len);
+ pru_request_cmd(pru_data_ram);
+ break;
+ case JTAG_TO_SWD:
+ LOG_DEBUG("JTAG-to-SWD");
+ pru_data_ram[0] = CMD_SIG_GEN | (swd_seq_jtag_to_swd_len << 8);
+ memcpy (&pru_data_ram[1], swd_seq_jtag_to_swd, swd_seq_jtag_to_swd_len);
+ pru_request_cmd(pru_data_ram);
+ bbg_swd_idle(8);
+ break;
+ case SWD_TO_JTAG:
+ LOG_DEBUG("JTAG-to-SWD");
+ pru_data_ram[0] = CMD_SIG_GEN | (swd_seq_swd_to_jtag_len << 8);
+ memcpy (&pru_data_ram[1], swd_seq_swd_to_jtag, swd_seq_swd_to_jtag_len);
+ pru_request_cmd(pru_data_ram);
+ break;
+ default:
+ LOG_ERROR("Sequence %d not supported", seq);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static void bbg_swd_clear_sticky_errors(void)
+{
+ bbg_swd_write_reg(swd_cmd(false, false, DP_ABORT),
+ STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
+}
+
+
+static void bbg_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
+{
+ LOG_DEBUG("bbg_swd_read_reg");
+ assert(cmd & SWD_CMD_RnW);
+ assert(ap_delay_clk < 256);
+
+ if (queued_retval != ERROR_OK) {
+ LOG_DEBUG("Skip bbg_swd_read_reg because queued_retval=%d", queued_retval);
+ return;
+ }
+
+ for (;;) {
+ int ack;
+ int parity;
+ uint32_t data;
+ uint32_t delay = 0;
+
+ if (cmd & SWD_CMD_APnDP)
+ delay = ap_delay_clk;
+
+ cmd |= 0x81;
+ pru_data_ram[0] = CMD_READ_REG | (cmd << 8) | (delay << 24);
+ pru_request_cmd(pru_data_ram);
+ ack = pru_data_ram[BBG_SWS_RESULT] & 0x07;
+ parity = (pru_data_ram[BBG_SWS_RESULT] & 0x80000000) != 0;
+ data = pru_data_ram[BBG_SWS_RESULT+1];
+
+ LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+ ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+ cmd & SWD_CMD_APnDP ? "AP" : "DP",
+ cmd & SWD_CMD_RnW ? "read" : "write",
+ (cmd & SWD_CMD_A32) >> 1,
+ data);
+
+ switch (ack) {
+ case SWD_ACK_OK:
+ if (parity != parity_u32(data)) {
+ LOG_DEBUG("Wrong parity detected (%d)", parity);
+ queued_retval = ERROR_FAIL;
+ return;
+ }
+ if (value)
+ *value = data;
+ return;
+ case SWD_ACK_WAIT:
+ LOG_DEBUG("SWD_ACK_WAIT");
+ bbg_swd_clear_sticky_errors();
+ break;
+ case SWD_ACK_FAULT:
+ LOG_DEBUG("SWD_ACK_FAULT");
+ queued_retval = ack;
+ return;
+ default:
+ LOG_DEBUG("No valid acknowledge: ack=%d", ack);
+ queued_retval = ack;
+ return;
+ }
+ }
+}
+
+static void bbg_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
+{
+ LOG_DEBUG("bbg_swd_write_reg");
+ assert(!(cmd & SWD_CMD_RnW));
+ assert(ap_delay_clk < 256);
+
+ if (queued_retval != ERROR_OK) {
+ LOG_DEBUG("Skip bbg_swd_write_reg because queued_retval=%d", queued_retval);
+ return;
+ }
+
+ for (;;) {
+ int ack;
+ int parity = parity_u32(value);
+ uint32_t delay = 0;
+
+ if (cmd & SWD_CMD_APnDP)
+ delay = ap_delay_clk;
+
+ cmd |= 0x81;
+ pru_data_ram[0] = CMD_WRITE_REG | (cmd << 8) | (parity << 16) | (delay << 24);
+ pru_data_ram[1] = value;
+ pru_request_cmd(pru_data_ram);
+ ack = pru_data_ram[BBG_SWS_RESULT] & 0x07;
+
+ LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+ ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+ cmd & SWD_CMD_APnDP ? "AP" : "DP",
+ cmd & SWD_CMD_RnW ? "read" : "write",
+ (cmd & SWD_CMD_A32) >> 1,
+ value);
+
+ switch (ack) {
+ case SWD_ACK_OK:
+ return;
+ case SWD_ACK_WAIT:
+ LOG_DEBUG("SWD_ACK_WAIT");
+ bbg_swd_clear_sticky_errors();
+ break;
+ case SWD_ACK_FAULT:
+ LOG_DEBUG("SWD_ACK_FAULT");
+ queued_retval = ack;
+ return;
+ default:
+ LOG_DEBUG("No valid acknowledge: ack=%d", ack);
+ queued_retval = ack;
+ return;
+ }
+ }
+}
+
+static int bbg_swd_run_queue(void)
+{
+ int retval;
+
+ LOG_DEBUG("bbg_swd_run_queue");
+ bbg_swd_idle(8);
+ retval = queued_retval;
+ queued_retval = ERROR_OK;
+ LOG_DEBUG("SWD queue return value: %02x", retval);
+ return retval;
+}
+
+const struct swd_driver bbg_swd = {
+ .init = bbg_swd_swd_init,
+ .switch_seq = bbg_swd_switch_seq,
+ .read_reg = bbg_swd_read_reg,
+ .write_reg = bbg_swd_write_reg,
+ .run = bbg_swd_run_queue,
+};
+
+
+static const char * const bbg_swd_transport[] = { "swd", NULL };
+
+
+COMMAND_HANDLER(bbg_swd_handle_hello_command)
+{
+ puts("Hello!");
+ return ERROR_OK;
+}
+
+static const struct command_registration bbg_swd_command_handlers[] = {
+ {
+ .name = "bbg_swd_hello",
+ .handler = &bbg_swd_handle_hello_command,
+ .mode = COMMAND_CONFIG,
+ .help = "hello command for BBG-SWD",
+ .usage = "<cmd>",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static void bbg_swd_execute_reset(struct jtag_command *cmd)
+{
+ bbg_swd_gpio_srst(cmd->cmd.reset->srst ? 0: 1);
+}
+
+static void bbg_swd_execute_sleep(struct jtag_command *cmd)
+{
+ jtag_sleep(cmd->cmd.sleep->us);
+}
+
+static void bbg_swd_execute_command(struct jtag_command *cmd)
+{
+ switch (cmd->type) {
+ case JTAG_RESET:
+ bbg_swd_execute_reset(cmd);
+ break;
+ case JTAG_SLEEP:
+ bbg_swd_execute_sleep(cmd);
+ break;
+ default:
+ LOG_ERROR("BUG: unknown JTAG command type encountered");
+ exit(-1);
+ }
+}
+
+static int bbg_swd_interface_execute_queue(void)
+{
+ struct jtag_command *cmd = jtag_command_queue;
+
+ while (cmd != NULL) {
+ bbg_swd_execute_command(cmd);
+ cmd = cmd->next;
+ }
+
+ return ERROR_OK;
+}
+
+static int bbg_swd_interface_speed(int speed)
+{
+ return ERROR_OK;
+}
+
+static int bbg_swd_interface_speed_div(int speed, int *khz)
+{
+ *khz = speed;
+ return ERROR_OK;
+}
+
+static int bbg_swd_interface_khz(int khz, int *jtag_speed)
+{
+ *jtag_speed = khz;
+ return ERROR_OK;
+}
+
+struct jtag_interface bbg_swd_interface = {
+ .name = "bbg-swd",
+ .commands = bbg_swd_command_handlers,
+ .swd = &bbg_swd,
+ .transports = bbg_swd_transport,
+
+ .execute_queue = bbg_swd_interface_execute_queue,
+ .speed = bbg_swd_interface_speed,
+ .speed_div = bbg_swd_interface_speed_div,
+ .khz = bbg_swd_interface_khz,
+
+ .init = bbg_swd_interface_init,
+ .quit = bbg_swd_interface_quit,
+};
+/*
+ * Local Variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/src/jtag/drivers/bbg-swd.h b/src/jtag/drivers/bbg-swd.h
new file mode 100644
index 0000000..f0c92b3
--- /dev/null
+++ b/src/jtag/drivers/bbg-swd.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2016 Flying Stone Technology *
+ * Author: NIIBE Yutaka <gniibe@fsij.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+ ***************************************************************************/
+
+#ifndef BBG_SWD_H
+#define BBG_SWD_H
+
+#include <jtag/swd.h>
+
+struct bbg_swd_interface {
+ /* low level callbacks (for bbg_swd)
+ */
+ int (*read)(void);
+ void (*write)(int tck, int tms, int tdi);
+ void (*reset)(int trst, int srst);
+ void (*blink)(int on);
+ int (*swdio_read)(void);
+ void (*swdio_drive)(bool on);
+};
+
+const struct swd_driver bbg_swd_swd;
+
+int bbg_swd_execute_queue(void);
+
+extern struct jtag_interface bbg_swd_interface;
+int bbg_swd_swd_switch_seq(enum swd_special_seq seq);
+
+#endif /* BBG_SWD_H */
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index 62c5d45..b3fb2bd 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -131,6 +131,9 @@ extern struct jtag_interface bcm2835gpio_interface;
#if BUILD_CMSIS_DAP == 1
extern struct jtag_interface cmsis_dap_interface;
#endif
+#if BUILD_BBG_SWD == 1
+extern struct jtag_interface bbg_swd_interface;
+#endif
#endif /* standard drivers */
/**
@@ -230,6 +233,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_CMSIS_DAP == 1
&cmsis_dap_interface,
#endif
+#if BUILD_BBG_SWD == 1
+ &bbg_swd_interface,
+#endif
#endif /* standard drivers */
NULL,
};
--
2.1.4
// -*- mode: asm -*-
// pru-swd.p - PRU program to handle SWD protocol
//
// Copyright (C) 2016 Flying Stone Technology
// Author: NIIBE Yutaka <gniibe@fsij.org>
//
// This file is a part of BBG-SWD, a SWD tool for BeagleBoneGreen.
//
// BBG-SWD is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// BBG-SWD is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
.origin 0
.entrypoint START
#define CONST_DELAY 47 // 470ns To be 1000 kHz
//
// DELAY - Macro for do nothing but wait
//
.macro DELAY
JAL r29.w0, DELAY_10NS
.endm
#define PRU0_ARM_INTERRUPT 19
#define ARM_PRU0_INTERRUPT 21
// Constant Table
#define CT_PRUCFG C4
#define CT_PRUDRAM C24
// PRU Control register
#define PRU0_CTRL 0x00022000 // address
#define WAKEUP_EN 8 // offset
// PRU CFG registers
#define SYSCFG 4 // offset
#define STANDBY_INIT 4 // bit
// PRU INTC registers
#define INTC 0x00020000 // address
#define INTC_SICR 0x24 // offset
// P8_11 GPIO1_13 GPIO_45 SWD_DIO
// P8_12 GPIO1_12 GPIO_44 SWD_CLK
// P8_15 GPIO1_15 GPIO_47 nRST
#define SWD_DIO_BIT 13
#define SWD_CLK_BIT 12
#define SWD_DIO (1<<SWD_DIO_BIT)
#define SWD_CLK (1<<SWD_CLK_BIT)
#define GPIO1_BASE_0100 0x4804c100
// offsets
#define GPIO_OE 0x34
#define GPIO_DATAIN 0x38
#define GPIO_CLEARDATAOUT 0x90
#define GPIO_SETDATAOUT 0x94
// LED
// bit 21: USR0, 22: USR1, 23: USR2, 24: USR3
// No operation but delay
#define NOP OR r0, r0, r0
#define CTBIR_0 0x22020
//
// DRIVE_CLK_HIGH - Macro to drive SWD_CLK "High"
//
.macro DRIVE_CLK_HIGH
SBBO r7, r5, GPIO_SETDATAOUT, 4
.endm
//
// DRIVE_CLK_LOW - Macro to drive SWD_CLK "Low"
//
.macro DRIVE_CLK_LOW
SBBO r7, r5, GPIO_CLEARDATAOUT, 4
.endm
//
// DRIVE_DIO_HIGH - Macro to drive SWD_DIO "High"
//
.macro DRIVE_DIO_HIGH
SBBO r6, r5, GPIO_SETDATAOUT, 4
.endm
//
// DRIVE_DIO_LOW - Macro to drive SWD_DIO "Low"
//
.macro DRIVE_DIO_LOW
SBBO r6, r5, GPIO_CLEARDATAOUT, 4
.endm
//
// TRN_INPUT - Macro to do TRN-bit for preparing input
//
.macro TRN_INPUT
DRIVE_CLK_LOW
SET_DIO_INPUT r2
DELAY
NOP
DRIVE_CLK_HIGH
DELAY
NOP
NOP
.endm
START:
// Enable OCP master port to access GPIO
LBCO r0, CT_PRUCFG, SYSCFG, 4
CLR r0, r0, STANDBY_INIT
SBCO r0, CT_PRUCFG, SYSCFG, 4
// Configure C24 to 0x00000000 (PRU0 DRAM)
LDI r0, #0
MOV r1, CTBIR_0
SBBO r0, r1, 0, 4
// Registers for constant values
MOV r5, #GPIO1_BASE_0100
LDI r6, #SWD_DIO
LDI r7, #SWD_CLK
// Initialize SWD_DIO and SWD_CLK pins
DRIVE_DIO_HIGH
DRIVE_CLK_HIGH
// SWD_DIO_oe <= Output, SWD_CLK_oe <= Output
LBBO r0, r5, GPIO_OE, 4
CLR r0, SWD_DIO_BIT
CLR r0, SWD_CLK_BIT
SBBO r0, r5, GPIO_OE, 4
// Wakeup control configuration
MOV r0, #PRU0_CTRL
MOV r1, #0xffffffff
SBBO r1, r0, WAKEUP_EN, 4
// Clear the counter
LDI r0, #0
SBCO r0, CT_PRUDRAM, 72, 4
QBA COMMAND_LOOP
//
// BLINK - Blink LED
//
BLINK:
LBCO r0, CT_PRUDRAM, 4, 12
//
// R0 = delay
// R1 = number of loops
// R2 = LED bit value
//
LOOP0:
SBBO r2, r5, GPIO_SETDATAOUT, 4
MOV r3, r0
LOOP1:
SUB r3, r3, 1
QBNE LOOP1, r3, 0
SBBO r2, r5, GPIO_CLEARDATAOUT, 4
MOV r3, r0
LOOP2:
SUB r3, r3, 1
QBNE LOOP2, r3, 0
SUB r1, r1, 1
QBNE LOOP0, r1, 0
LDI r0, #0
SBCO r0, CT_PRUDRAM, 64, 4
QBA COMMAND_DONE
//
// GPIO_OUT - Output to GPIO pin
//
GPIO_OUT:
//
// R0 = bit-value
// R1 = value
//
LBCO r0, CT_PRUDRAM, 4, 8
//
QBBS L_GPIO_OUT_1, r1.t0
SBBO r0, r5, GPIO_CLEARDATAOUT, 4
QBA L_GPIO_OUT_DONE
L_GPIO_OUT_1:
SBBO r0, r5, GPIO_SETDATAOUT, 4
NOP
L_GPIO_OUT_DONE:
//
LDI r0, #0
SBCO r0, CT_PRUDRAM, 64, 4
QBA COMMAND_DONE
//
// GPIO_IN - Input from GPIO pin
//
GPIO_IN:
LBBO r0, r5, GPIO_DATAIN, 4
//
// RETURN: Value
SBCO r0, CT_PRUDRAM, 64, 4
QBA COMMAND_DONE
//
// SET_DIO_OUTPUT - Macro to set mode of SWD_DIO to output
//
.macro SET_DIO_OUTPUT
.mparam rx
// SWD_DIO_oe <= Output
LBBO rx, r5, GPIO_OE, 4
CLR rx, SWD_DIO_BIT
SBBO rx, r5, GPIO_OE, 4
.endm
//
// SET_DIO_INPUT - Macro to set mode of SWD_DIO to input
//
.macro SET_DIO_INPUT
.mparam rx
// SWD_DIO_oe <= Input
LBBO rx, r5, GPIO_OE, 4
SET rx, SWD_DIO_BIT
SBBO rx, r5, GPIO_OE, 4
.endm
DO_SIG_IDLE:
L_SIG_IDLE:
DRIVE_CLK_LOW
DELAY
NOP
NOP
DRIVE_CLK_HIGH
DELAY
SUB r0, r0, 1
QBNE L_SIG_IDLE, r0, 0
RET
//
// SIG_IDLE - Park SWD_DIO = Low and strobe SWD_CLK
//
SIG_IDLE:
//
// R0 = count
//
LBCO r0, CT_PRUDRAM, 4, 4
//
DRIVE_DIO_LOW
//
JAL r30.w0, DO_SIG_IDLE
//
DRIVE_DIO_HIGH
//
LDI r0, #0
SBCO r0, CT_PRUDRAM, 64, 4
QBA COMMAND_DONE
//
// SIG_GEN - Generate signal pattern on SWD_DIO with SWD_CLK strobe
//
SIG_GEN:
//
// R0 = bit-count
//
LDI r0, #0
LBCO r0.b0, CT_PRUDRAM, 1, 1
//
// R16..R23: Bit pattern (256-bit maximum)
//
LBCO r16, CT_PRUDRAM, 4, 32
//
// Start with r16, from LSB
MOV r1.b0, &r16
LDI r2, #1
MVID r3, *r1.b0++
L_GEN_LOOP:
SUB r0, r0, 1
LSL r2, r2, 1
QBBS L_GEN_BIT1, r3.t0
LSR r3, r3, 1
DRIVE_CLK_LOW
DRIVE_DIO_LOW
QBA L_GEN_BIT_DONE
//
L_NO_LOAD:
NOP
QBA L_NEXT_BIT
//
L_GEN_BIT1:
LSR r3, r3, 1
DRIVE_CLK_LOW
DRIVE_DIO_HIGH
NOP
L_GEN_BIT_DONE:
//
DELAY
QBNE L_NO_LOAD, r2, 0
MVID r3, *r1.b0++
LDI r2, #1
L_NEXT_BIT:
DRIVE_CLK_HIGH
DELAY
QBNE L_GEN_LOOP, r0, 0
//
L_SIG_GEN_DONE:
LDI r0, #0
SBCO r0, CT_PRUDRAM, 64, 4
//
DRIVE_DIO_HIGH
QBA COMMAND_DONE
///////////////////////////////////////////////////////////////////////////
COMMAND_DONE:
// Increment the counter
LBCO r0, CT_PRUDRAM, 72, 4
ADD r0, r0, #1
SBCO r0, CT_PRUDRAM, 72, 4
// Clear the event
MOV r1, #INTC
LDI r2, #ARM_PRU0_INTERRUPT
SBBO r2, r1, INTC_SICR, 4
// Notify Host
MOV r31.b0, PRU0_ARM_INTERRUPT+16
COMMAND_LOOP:
// Wait until host wakes up PRU0
SLP 1
// Load values from data RAM into register R0
LBCO r0, CT_PRUDRAM, 0, 1
QBBS L_1xx, r0.t2
QBBS L_01x, r0.t1
QBBS L_001, r0.t0
L_000: // Command HALT
MOV r0, #0
SBCO r0, CT_PRUDRAM, 64, 4
MOV r31.b0, PRU0_ARM_INTERRUPT+16
HALT
L_001: // Command BLINK
QBA BLINK
L_01x:
QBBS L_011, r0.t0
L_010: // Command GPIO_OUT
QBA GPIO_OUT
L_011: // Command GPIO_IN
QBA GPIO_IN
L_1xx:
QBBS L_11x, r0.t1
QBBS L_101, r0.t0
L_100: // Command SIG_IDLE
QBA SIG_IDLE
L_101: // Command SIG_GEN
QBA SIG_GEN
L_11x:
QBBS L_111, r0.t0
L_110: // Command READ_REG
QBA READ_REG
L_111: // Command WRITE_REG
QBA WRITE_REG
///////////////////////////////////////////////////////////////////////////
DELAY_10NS: // delay_clocks = CONST_DELAY * 2 + 2
LDI r8, #CONST_DELAY
L_DELAY:
SUB r8, r8, 1
QBNE L_DELAY, r8, 1
JMP r29.w0
//
// WRITE_SWD_DIO_BIT_NO_LAST_NOP - Macro writing SWD_DIO bit, but NOP
//
.macro WRITE_SWD_DIO_BIT_NO_LAST_NOP
.mparam src_bit, label_bit1, label_done
QBBS label_bit1, src_bit
DRIVE_CLK_LOW
DRIVE_DIO_LOW
QBA label_done
label_bit1:
DRIVE_CLK_LOW
DRIVE_DIO_HIGH
NOP
label_done:
DELAY
DRIVE_CLK_HIGH // <---- Target read
DELAY
.endm
//
// WRITE_SWD_DIO_BIT_NO_LAST_NOP - Macro writing SWD_DIO bit, with NOP
//
.macro WRITE_SWD_DIO_BIT
.mparam src_bit, label_bit1, label_done
WRITE_SWD_DIO_BIT_NO_LAST_NOP src_bit, label_bit1, label_done
NOP
.endm
//
// READ_SWD_DIO_BIT - Macro reading SWD_DIO bit onto register Rx
//
.macro READ_SWD_DIO_BIT
.mparam rx, ry, label_1, label_done, shift=1
DRIVE_CLK_LOW
DELAY
LBBO ry, r5, GPIO_DATAIN, 4
LSR rx, rx, shift
DRIVE_CLK_HIGH
QBBS label_1, ry, SWD_DIO_BIT
QBA label_done
label_1:
SET rx, 31
label_done:
DELAY
.endm
//
// READ_REG - execute READ_REG transaction
//
READ_REG:
//
// R0 = command
//
LDI r0, #0
LBCO r0.b0, CT_PRUDRAM, 1, 1
LBCO r0.b2, CT_PRUDRAM, 3, 1
//
//
//
WRITE_SWD_DIO_BIT r0.t0, L_RRC0_BIT1, L_RRC0_DONE
WRITE_SWD_DIO_BIT r0.t1, L_RRC1_BIT1, L_RRC1_DONE
WRITE_SWD_DIO_BIT r0.t2, L_RRC2_BIT1, L_RRC2_DONE
WRITE_SWD_DIO_BIT r0.t3, L_RRC3_BIT1, L_RRC3_DONE
WRITE_SWD_DIO_BIT r0.t4, L_RRC4_BIT1, L_RRC4_DONE
WRITE_SWD_DIO_BIT r0.t5, L_RRC5_BIT1, L_RRC5_DONE
WRITE_SWD_DIO_BIT r0.t6, L_RRC6_BIT1, L_RRC6_DONE
WRITE_SWD_DIO_BIT r0.t7, L_RRC7_BIT1, L_RRC7_DONE
NOP
//
TRN_INPUT
// Read ACK bits onto R3
READ_SWD_DIO_BIT r3, r2, L_RRD0_1, L_RRD0_F
READ_SWD_DIO_BIT r3, r2, L_RRD1_1, L_RRD1_F
READ_SWD_DIO_BIT r3, r2, L_RRD2_1, L_RRD2_F
// Read RDATA bits onto R4
READ_SWD_DIO_BIT r4, r2, L_RRD3_1, L_RRD3_F
READ_SWD_DIO_BIT r4, r2, L_RRD4_1, L_RRD4_F
READ_SWD_DIO_BIT r4, r2, L_RRD5_1, L_RRD5_F
READ_SWD_DIO_BIT r4, r2, L_RRD6_1, L_RRD6_F
READ_SWD_DIO_BIT r4, r2, L_RRD7_1, L_RRD7_F
READ_SWD_DIO_BIT r4, r2, L_RRD8_1, L_RRD8_F
READ_SWD_DIO_BIT r4, r2, L_RRD9_1, L_RRD9_F
READ_SWD_DIO_BIT r4, r2, L_RRDa_1, L_RRDa_F
//
READ_SWD_DIO_BIT r4, r2, L_RRDb_1, L_RRDb_F
READ_SWD_DIO_BIT r4, r2, L_RRDc_1, L_RRDc_F
READ_SWD_DIO_BIT r4, r2, L_RRDd_1, L_RRDd_F
READ_SWD_DIO_BIT r4, r2, L_RRDe_1, L_RRDe_F
READ_SWD_DIO_BIT r4, r2, L_RRDf_1, L_RRDf_F
READ_SWD_DIO_BIT r4, r2, L_RRDg_1, L_RRDg_F
READ_SWD_DIO_BIT r4, r2, L_RRDh_1, L_RRDh_F
READ_SWD_DIO_BIT r4, r2, L_RRDi_1, L_RRDi_F
//
READ_SWD_DIO_BIT r4, r2, L_RRDj_1, L_RRDj_F
READ_SWD_DIO_BIT r4, r2, L_RRDk_1, L_RRDk_F
READ_SWD_DIO_BIT r4, r2, L_RRDl_1, L_RRDl_F
READ_SWD_DIO_BIT r4, r2, L_RRDm_1, L_RRDm_F
READ_SWD_DIO_BIT r4, r2, L_RRDn_1, L_RRDn_F
READ_SWD_DIO_BIT r4, r2, L_RRDo_1, L_RRDo_F
READ_SWD_DIO_BIT r4, r2, L_RRDp_1, L_RRDp_F
READ_SWD_DIO_BIT r4, r2, L_RRDq_1, L_RRDq_F
//
READ_SWD_DIO_BIT r4, r2, L_RRDr_1, L_RRDr_F
READ_SWD_DIO_BIT r4, r2, L_RRDs_1, L_RRDs_F
READ_SWD_DIO_BIT r4, r2, L_RRDt_1, L_RRDt_F
READ_SWD_DIO_BIT r4, r2, L_RRDu_1, L_RRDu_F
READ_SWD_DIO_BIT r4, r2, L_RRDv_1, L_RRDv_F
READ_SWD_DIO_BIT r4, r2, L_RRDw_1, L_RRDw_F
READ_SWD_DIO_BIT r4, r2, L_RRDx_1, L_RRDx_F
READ_SWD_DIO_BIT r4, r2, L_RRDy_1, L_RRDy_F
// Parity bit
READ_SWD_DIO_BIT r3, r2, L_RRDz_1, L_RRDz_F, 29
// TRN
DRIVE_CLK_LOW
DELAY
NOP
NOP
DRIVE_CLK_HIGH
//
DELAY
LSR r0, r0, 16
QBEQ L_SKIP_IDLE_R, r0, 0
DRIVE_DIO_LOW
SET_DIO_OUTPUT r2
JAL r30.w0, DO_SIG_IDLE
//
L_SKIP_IDLE_R:
DRIVE_DIO_HIGH
SET_DIO_OUTPUT r2
// RETURN: Parity|Ack, Value
SBCO r3, CT_PRUDRAM, 64, 8
QBA COMMAND_DONE
//
// WRITE_REG - execute WRITE_REG transaction
//
WRITE_REG:
//
// R0 = command + parity_as_t8
// R1 = value
//
LDI r0, #0
LBCO r0.b0, CT_PRUDRAM, 1, 1
LBCO r0.b1, CT_PRUDRAM, 2, 1
LBCO r0.b2, CT_PRUDRAM, 3, 1
LBCO r1, CT_PRUDRAM, 4, 8
//
//
WRITE_SWD_DIO_BIT r0.t0, L_WRC0_BIT1, L_WRC0_DONE
WRITE_SWD_DIO_BIT r0.t1, L_WRC1_BIT1, L_WRC1_DONE
WRITE_SWD_DIO_BIT r0.t2, L_WRC2_BIT1, L_WRC2_DONE
WRITE_SWD_DIO_BIT r0.t3, L_WRC3_BIT1, L_WRC3_DONE
WRITE_SWD_DIO_BIT r0.t4, L_WRC4_BIT1, L_WRC4_DONE
WRITE_SWD_DIO_BIT r0.t5, L_WRC5_BIT1, L_WRC5_DONE
WRITE_SWD_DIO_BIT r0.t6, L_WRC6_BIT1, L_WRC6_DONE
WRITE_SWD_DIO_BIT r0.t7, L_WRC7_BIT1, L_WRC7_DONE
NOP
//
TRN_INPUT
// Read ACK bits onto R3
READ_SWD_DIO_BIT r3, r2, L_WRA0_1, L_WRA0_F
READ_SWD_DIO_BIT r3, r2, L_WRA1_1, L_WRA1_F
READ_SWD_DIO_BIT r3, r2, L_WRA2_1, L_WRA2_F
//
// TRN and WRITE the first bit
DRIVE_CLK_LOW
DELAY
NOP
QBBS L_WRD0_BIT1, r1.t0
DRIVE_CLK_HIGH
DRIVE_DIO_LOW
QBA L_WRD0_DONE
L_WRD0_BIT1:
DRIVE_CLK_HIGH
DRIVE_DIO_HIGH
NOP
L_WRD0_DONE:
DELAY
DRIVE_CLK_LOW
SET_DIO_OUTPUT r2
DELAY
NOP
DRIVE_CLK_HIGH
DELAY
NOP
//
WRITE_SWD_DIO_BIT r1.t1, L_WRD1_BIT1, L_WRD1_DONE
WRITE_SWD_DIO_BIT r1.t2, L_WRD2_BIT1, L_WRD2_DONE
WRITE_SWD_DIO_BIT r1.t3, L_WRD3_BIT1, L_WRD3_DONE
WRITE_SWD_DIO_BIT r1.t4, L_WRD4_BIT1, L_WRD4_DONE
WRITE_SWD_DIO_BIT r1.t5, L_WRD5_BIT1, L_WRD5_DONE
WRITE_SWD_DIO_BIT r1.t6, L_WRD6_BIT1, L_WRD6_DONE
WRITE_SWD_DIO_BIT r1.t7, L_WRD7_BIT1, L_WRD7_DONE
WRITE_SWD_DIO_BIT r1.t8, L_WRD8_BIT1, L_WRD8_DONE
WRITE_SWD_DIO_BIT r1.t9, L_WRD9_BIT1, L_WRD9_DONE
WRITE_SWD_DIO_BIT r1.t10, L_WRDa_BIT1, L_WRDa_DONE
WRITE_SWD_DIO_BIT r1.t11, L_WRDb_BIT1, L_WRDb_DONE
WRITE_SWD_DIO_BIT r1.t12, L_WRDc_BIT1, L_WRDc_DONE
WRITE_SWD_DIO_BIT r1.t13, L_WRDd_BIT1, L_WRDd_DONE
WRITE_SWD_DIO_BIT r1.t14, L_WRDe_BIT1, L_WRDe_DONE
WRITE_SWD_DIO_BIT r1.t15, L_WRDf_BIT1, L_WRDf_DONE
WRITE_SWD_DIO_BIT r1.t16, L_WRDg_BIT1, L_WRDg_DONE
WRITE_SWD_DIO_BIT r1.t17, L_WRDh_BIT1, L_WRDh_DONE
WRITE_SWD_DIO_BIT r1.t18, L_WRDi_BIT1, L_WRDi_DONE
WRITE_SWD_DIO_BIT r1.t19, L_WRDj_BIT1, L_WRDj_DONE
WRITE_SWD_DIO_BIT r1.t20, L_WRDk_BIT1, L_WRDk_DONE
WRITE_SWD_DIO_BIT r1.t21, L_WRDl_BIT1, L_WRDl_DONE
WRITE_SWD_DIO_BIT r1.t22, L_WRDm_BIT1, L_WRDm_DONE
WRITE_SWD_DIO_BIT r1.t23, L_WRDn_BIT1, L_WRDn_DONE
WRITE_SWD_DIO_BIT r1.t24, L_WRDo_BIT1, L_WRDo_DONE
WRITE_SWD_DIO_BIT r1.t25, L_WRDp_BIT1, L_WRDp_DONE
WRITE_SWD_DIO_BIT r1.t26, L_WRDq_BIT1, L_WRDq_DONE
WRITE_SWD_DIO_BIT r1.t27, L_WRDr_BIT1, L_WRDr_DONE
WRITE_SWD_DIO_BIT r1.t28, L_WRDs_BIT1, L_WRDs_DONE
WRITE_SWD_DIO_BIT r1.t29, L_WRDt_BIT1, L_WRDt_DONE
WRITE_SWD_DIO_BIT r1.t30, L_WRDu_BIT1, L_WRDu_DONE
WRITE_SWD_DIO_BIT r1.t31, L_WRDv_BIT1, L_WRDv_DONE
WRITE_SWD_DIO_BIT_NO_LAST_NOP r0.t8, L_WRDw_BIT1, L_WRDw_DONE
//
LSR r0, r0, 16
QBEQ L_SKIP_IDLE_W, r0, 0
DRIVE_DIO_LOW
JAL r30.w0, DO_SIG_IDLE
//
L_SKIP_IDLE_W:
DRIVE_DIO_HIGH
// RETURN: Ack
LSR r3, r3, 29
SBCO r3, CT_PRUDRAM, 64, 4
JMP COMMAND_DONE
//
// Local Variables:
// compile-command: "pasm -V3 -l -b pru-swd.p"
// End:
//
Comments