Yutaka Niibe
Published © GPL3+

BBG-SWD

Make our BBG into SWD programmer by its powerful PRUSS (Progrmmable Reaitime Unit SubSystem), in order to control our bits.

ExpertShowcase (no instructions)2 hours1,652
BBG-SWD

Things used in this project

Story

Read more

Schematics

BBG-SWD connection

This drawing explains how to connect BBG for SWD connection.

Code

0001-initial-patch-for-BBG-SWD.patch

C/C++
This is a patch for OpenOCD to support BBG-SWD driver.
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

PRU-SWD.p

Assembly x86
Code for PRUSS to control SWD signal.
//							-*- 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:
//

Credits

Yutaka Niibe

Yutaka Niibe

2 projects • 7 followers
GNU/Linux developper, GnuPG, Gnuk, FST-01 and FS-BB48
Thanks to Kaz Kojima.

Comments