[Из песочницы] Разгоняем JTAG роутер

Если вдруг вам потребуется отлаживать несколько микроконтроллеров/микропроцессоров в Крыму, попивая смузи из душного офиса в Химках. Если микропроцессорная плата находится на подвижном объекте и нет возможности дотянуть до нее JTAG отладчик (плата находится на воздушном шарике/квадрокоптере). Если вдруг просто требуется гальваническая изоляция между хостом и отлаживаемой платой (допустим, высоковольтное устройство). И хорошо, что бы еще дешево и сердито. Тогда вам потребуется роутер, да-да, просто роутер, допустим, вот такой.

image
Картинка с сайта sagemcom.ru

Заглянем внутрь:

image
wiki.openwrt.org

Итак, это Sagem F@ST2704 V2, распространяемый Ростелекомом по всей стране. Имеем SoC BCM6328 ядро MIPS архитектуры, 320 MHz, пара распаянных USB портов [1]. Есть wifi и ethernet. И самое приятное — это релиз openwrt на эту модель. Всё, что нужно из оборудования для поставленных выше целей.

Сразу возникает мысль подцепить st-link и попробовать пробросить USB через сеть. Выглядит костыльно, скорее всего обещает работать не быстро и не очень стабильно, оверхед получается громадный. Смотрим дальше, что можно сделать.

Можно портировать openocd на openwrt, подцепить st-link или ftdi-микросхему и запустить gdb-сервер. Благо в openwrt уже портировали openocd. Вроде достаточно уже остановиться на этом варианте. Но хочется посмотреть еще какие варианты нам дает openocd. И тут в документации попадается интерфейс sysfsgpio. То, что нужно, возможно управлять сигналами tck, tdi, tdo, штатными средствами OS linux через /sys/class/gpio на распаянных пинах чипа.

Пробуем. Для начала собираем openwrt (использую ветку chaos_calmer) вместе c openocd. По умолчанию на распаянных GPIO закреплены функции световой индикации, а так же опрос кнопок для выполнения некоторых команд (rfkill,reset и wpsc ). Чтобы они не мешались, я их выключил, убрав из сборки соответствующие модули ядра.

$cat target/linux/brcm63xx/config-3.18 b/target/linux/brcm63xx/config-3.18 ... # CONFIG_NEW_LEDS is not set … $cat .config …  # CONFIG_PACKAGE_kmod-input-gpio-keys-polled is not set ... # CONFIG_PACKAGE_kmod-input-polldev is not set ... CONFIG_PACKAGE_openocd=y 

сама сборка:

./scripts/feeds update -a ./scripts/feeds install -a make V=s  

Прошивка:

mtd -q write openwrt-brcm63xx-generic-F@ST2704V2-squashfs-cfe.bin linux 

Для теста sysfsgpio составляем конфиг:

root@OpenWrt:~# cat sysfs.cfg.2.11  interface sysfsgpio transport select swd  sysfsgpio_swclk_num 482 sysfsgpio_swdio_num 491  source [find target/stm32f1x.cfg] 

Подсоединяем как на фото:

Запускаем:

root@OpenWrt:~# openocd -f sysfs.cfg.2.11
Open On-Chip Debugger 0.10.0+dev-00085-gfced6ac6-dirty (2017-03-xx-21:49)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
SysfsGPIO num: swclk = 482
SysfsGPIO num: swdio = 491
SysfsGPIO num: trst = 481
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
cortex_m reset_config sysresetreq
Info : SysfsGPIO JTAG/SWD bitbang driver
Info : SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)
Warn : gpio 482 is already exported
Warn : gpio 491 is already exported
Info : This adapter doesn't support configurable speed
Info : SWD DPIDR 0x1ba01477
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints

Запускаем дебаг в IDE, все работает.

Только очень медленно.

Пробуем оценить количественно скорость, заходим по телнет на роутер:

telnet 10.65.9.239 4444 

Делаем дамп памяти.

> dump_image dump.bin 0x08000000 0x1ffff dumped 131071 bytes in 55.013523s (2.327 KiB/s) 

Мда, к примеру st-linkv2 у меня на хосте выдает скорость порядка 45 KiB/s. 20 раз разница!

Дело, конечно же, из-за медленной работы с файлами в /sys/class/gpio. Ковыряемся дальше в openocd. Находим интерфейсный драйвер для RaspberryPi (src/jtag/drivers/bcm2835gpio.c). Судя по тестам [5], скорость у него должна быть как примерно у st-link. Это достигнуто, во многом, благодаря прямому обращению к регистрам GPIO. Сделаем тоже самое и для нашего SoC, а так же это будет справедливо для всего семейства чипов bcm63xx.

получился вот такой интерфейс

/**  * @file  * This driver implements a bitbang jtag interface using gpio lines via  * router ob BCM63XX SoC.  * The aim of this driver implementation is use system GPIOs but avoid the  * need for a additional kernel driver.  * (Note memory mapped IO is another option, however it doesn't mix well with  * the kernel gpiolib driver - which makes sense I guess.)  *  * A gpio is required for tck, tms, tdi and tdo. One or both of srst and trst  * must be also be specified. The required jtag gpios are specified via the  * bcm63xx_gpio_jtag_nums command or the relevant bcm63xx_gpio_XXX_num commang.  * The srst and trst gpios are set via the bcm63xx_gpio_srst_num and  * bcm63xx_gpio_trst_num respectively. GPIO numbering follows the kernel  * convention of starting from 0.  *  * The gpios should not be in use by another entity, and must not be requested  * by a kernel driver without also being exported by it (otherwise they can't  * be exported by bcm63xx_).  *  * The bcm63xx gpio interface can only manipulate one gpio at a time, so the  * bitbang write handler remembers the last state for tck, tms, tdi to avoid  * superfluous writes.  * For speed the bcm63xx "value" entry is opened at init and held open.  * This results in considerable gains over open-write-close (45s vs 900s)  *  * Further work could address:  *  -srst and trst open drain/ push pull  *  -configurable active high/low for srst & trst  */ #ifdef HAVE_CONFIG_H #include "config.h" #endif  #include  #include "bitbang.h"  #include   /*  * Helper func to determine if gpio number valid  *  * Assume here that there will be less than 1000 gpios on a system  */ static int is_gpio_valid(int gpio) { 	return gpio >= 0 && gpio < 32; }  off_t address_dir = NULL; off_t address_val = NULL;  static int dev_mem_fd = -1; static volatile uint32_t *pio_base = NULL; static volatile uint32_t *pval_base = NULL; static volatile uint32_t *pads_base = NULL; static unsigned int jtag_delay = 0;   static void set_dir_gpio(const int gpio, const int direction) { 	if(direction) 		*pio_base |= 1 << gpio; 	else 		*pio_base &= ~(1 << gpio); }   static void set_value_gpio(const int gpio, const int value) { 	if(value) 		*pval_base |= 1 << gpio; 	else 		*pval_base &= ~(1 << gpio);  	for (unsigned int i = 0; i < jtag_delay; i++) 		asm volatile (""); }  static int read_gpio(const int gpio) { 	uint32_t val = *pval_base & (1 << gpio); 	val = val ? 1 : 0; 	return val; }  static int setup_bcm63xx_gpio(int gpio, int is_output, int init_high) { 	char buf[40]; 	char gpiostr[4]; 	int ret;  	if (!is_gpio_valid(gpio)) 		return ERROR_OK;  	if((address_dir == NULL) || (address_val == NULL)){ 		perror("address of gpio register don't set"); 		return ERROR_FAIL;  	}   	if( dev_mem_fd < 0 ) 	{  		dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC); 		if (dev_mem_fd < 0) { 			perror("open"); 			return ERROR_FAIL; 		}  		const uint32_t mapped_size = getpagesize();  		const off_t target_mmap = address_dir & ~(off_t)(mapped_size - 1);  		pads_base = mmap(NULL, mapped_size, PROT_READ | PROT_WRITE, 					MAP_SHARED, dev_mem_fd, target_mmap);  		if (pads_base == MAP_FAILED) { 			perror("mmap. Check correct register address."); 			close(dev_mem_fd); 			return ERROR_FAIL; 		}  		pio_base 	= (char*)pads_base + (unsigned)(address_dir -  target_mmap); 		pval_base 	= (char*)pads_base + (unsigned)(address_val - target_mmap); 	}  	set_dir_gpio(gpio, is_output); 	set_value_gpio(gpio, init_high);  	return 0; }  /* gpio numbers for each gpio. Negative values are invalid */ static int tck_gpio = -1; static int tms_gpio = -1; static int tdi_gpio = -1; static int tdo_gpio = -1; static int trst_gpio = -1; static int srst_gpio = -1; static int swclk_gpio = -1; static int swdio_gpio = -1;  /*  * file descriptors for /sys/class/gpio/gpioXX/value  * Set up during init.  */ static int tck_fd = -1; static int tms_fd = -1; static int tdi_fd = -1; static int tdo_fd = -1; static int trst_fd = -1; static int srst_fd = -1; static int swclk_fd = -1; static int swdio_fd = -1;  static int last_swclk; static int last_swdio; static bool last_stored; static bool swdio_input;  static void bcm63xx_gpio_swdio_drive(bool is_output) { 	set_dir_gpio(swdio_gpio, is_output ? 1 : 0);  	last_stored = false; 	swdio_input = !is_output; }  static int bcm63xx_gpio_swdio_read(void) { 	return read_gpio(swdio_gpio); }  static void bcm63xx_gpio_swdio_write(int swclk, int swdio) {  	const char one[] = "1"; 	const char zero[] = "0";  	size_t bytes_written;  	if (!swdio_input) { 		if (!last_stored || (swdio != last_swdio)) { 			set_value_gpio(swdio_gpio, swdio ? 1 : 0); 		}  	}  	/* write swclk last */ 	if (!last_stored || (swclk != last_swclk)) { 			set_value_gpio(swclk_gpio, swclk ? 1 : 0); 	}  	last_swdio = swdio; 	last_swclk = swclk; 	last_stored = true; }  /*  * Bitbang interface read of TDO  *  * The bcm63xx value will read back either '0' or '1'. The trick here is to call  * lseek to bypass buffering in the bcm63xx kernel driver.  */ static int bcm63xx_gpio_read(void) { 	return read_gpio(tdo_gpio); }  /*  * Bitbang interface write of TCK, TMS, TDI  *  * Seeing as this is the only function where the outputs are changed,  * we can cache the old value to avoid needlessly writing it.  */ static void bcm63xx_gpio_write(int tck, int tms, int tdi) { 	if (swd_mode) { 		bcm63xx_gpio_swdio_write(tck, tdi); 		return; 	}  	const char one[] = "1"; 	const char zero[] = "0";  	static int last_tck; 	static int last_tms; 	static int last_tdi;  	static int first_time; 	size_t bytes_written;  	if (!first_time) { 		last_tck = !tck; 		last_tms = !tms; 		last_tdi = !tdi; 		first_time = 1; 	}  	if (tdi != last_tdi) { 		set_value_gpio(tdi_gpio,tdi); 	}  	if (tms != last_tms) { 		set_value_gpio(tms_gpio,tms); 	}  	/* write clk last */ 	if (tck != last_tck) { 		set_value_gpio(tck_gpio,tck);  	}  	last_tdi = tdi; 	last_tms = tms; 	last_tck = tck; }  /*  * Bitbang interface to manipulate reset lines SRST and TRST  *  * (1) assert or (0) deassert reset lines  */ static void bcm63xx_gpio_reset(int trst, int srst) { 	LOG_DEBUG("bcm63xx_gpio_reset"); 	const char one[] = "1"; 	const char zero[] = "0"; 	size_t bytes_written;  	/* assume active low */ 	if (srst_fd >= 0) { 		set_value_gpio(srst_gpio,srst); 	}  	/* assume active low */ 	if (trst_fd >= 0) { 		set_value_gpio(trst_gpio,trst); 	} }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionums) { 	if (CMD_ARGC == 4) { 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio); 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio); 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio); 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio); 	} else if (CMD_ARGC != 0) { 		return ERROR_COMMAND_SYNTAX_ERROR; 	}  	command_print(CMD_CTX, 			"bcm63xx_GPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d", 			tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);  	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tck) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: tck = %d", tck_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tms) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: tms = %d", tms_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tdo) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: tdo = %d", tdo_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_tdi) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: tdi = %d", tdi_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_srst) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: srst = %d", srst_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_jtag_gpionum_trst) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: trst = %d", trst_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_swd_gpionums) { 	if (CMD_ARGC == 2) { 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio); 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio); 	} else if (CMD_ARGC != 0) { 		return ERROR_COMMAND_SYNTAX_ERROR; 	}  	command_print(CMD_CTX, 			"bcm63xx_GPIO nums: swclk = %d, swdio = %d", 			swclk_gpio, swdio_gpio);  	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_swd_gpionum_swclk) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: swclk = %d", swclk_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_handle_swd_gpionum_swdio) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);  	command_print(CMD_CTX, "bcm63xx_GPIO num: swdio = %d", swdio_gpio); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_jtag_delay) { 	if (CMD_ARGC == 1) 		COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], jtag_delay);  	command_print(CMD_CTX, "bcm63xx_GPIO jtag_delay:= %d tics", jtag_delay); 	return ERROR_OK; }  COMMAND_HANDLER(bcm63xx_gpio_adresses) { 	if (CMD_ARGC == 2) { 		COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address_dir); 		COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], address_val); 	} else if (CMD_ARGC != 0) { 		return ERROR_COMMAND_SYNTAX_ERROR; 	}  	command_print(CMD_CTX, 			"bcm63xx_GPIO address: direction = %x, value = %x", 			address_dir, address_val);  	return ERROR_OK; }   static const struct command_registration bcm63xx_gpio_command_handlers[] = { 	{ 		.name = "bcm63xx_gpio_jtag_nums", 		.handler = &bcm63xx_gpio_handle_jtag_gpionums, 		.mode = COMMAND_CONFIG, 		.help = "gpio numbers for tck, tms, tdi, tdo. (in that order)", 		.usage = "(tck tms tdi tdo)* ", 	}, 	{ 		.name = "bcm63xx_gpio_tck_num", 		.handler = &bcm63xx_gpio_handle_jtag_gpionum_tck, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for tck.", 	}, 	{ 		.name = "bcm63xx_gpio_tms_num", 		.handler = &bcm63xx_gpio_handle_jtag_gpionum_tms, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for tms.", 	}, 	{ 		.name = "bcm63xx_gpio_tdo_num", 		.handler = &bcm63xx_gpio_handle_jtag_gpionum_tdo, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for tdo.", 	}, 	{ 		.name = "bcm63xx_gpio_tdi_num", 		.handler = &bcm63xx_gpio_handle_jtag_gpionum_tdi, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for tdi.", 	}, 	{ 		.name = "bcm63xx_gpio_srst_num", 		.handler = &bcm63xx_gpio_handle_jtag_gpionum_srst, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for srst.", 	}, 	{ 		.name = "bcm63xx_gpio_trst_num", 		.handler = &bcm63xx_gpio_handle_jtag_gpionum_trst, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for trst.", 	}, 	{ 		.name = "bcm63xx_gpio_swd_nums", 		.handler = &bcm63xx_gpio_handle_swd_gpionums, 		.mode = COMMAND_CONFIG, 		.help = "gpio numbers for swclk, swdio. (in that order)", 		.usage = "(swclk swdio)* ", 	}, 	{ 		.name = "bcm63xx_gpio_swclk_num", 		.handler = &bcm63xx_gpio_handle_swd_gpionum_swclk, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for swclk.", 	}, 	{ 		.name = "bcm63xx_gpio_swdio_num", 		.handler = &bcm63xx_gpio_handle_swd_gpionum_swdio, 		.mode = COMMAND_CONFIG, 		.help = "gpio number for swdio.", 	}, 	{ 		.name = "bcm63xx_gpio_jtag_delay", 		.handler = &bcm63xx_gpio_jtag_delay, 		.mode = COMMAND_CONFIG, 		.help = "qty tics gpio delay.", 	}, 	{ 		.name = "bcm63xx_gpio_adresses", 		.handler = &bcm63xx_gpio_adresses, 		.mode = COMMAND_CONFIG, 		.help = "addresses for direction and value setup. (in that order)", 		.usage = "(address_dir address_val)* ", 	}, 	COMMAND_REGISTRATION_DONE };  static int bcm63xx_gpio_init(void); static int bcm63xx_gpio_quit(void);  static const char * const bcm63xx_gpio_transports[] = { "jtag", "swd", NULL };  struct jtag_interface bcm63xxgpio_interface = { 	.name = "bcm63xxgpio", 	.supported = DEBUG_CAP_TMS_SEQ, 	.execute_queue = bitbang_execute_queue, 	.transports = bcm63xx_gpio_transports, 	.swd = &bitbang_swd, 	.commands = bcm63xx_gpio_command_handlers, 	.init = bcm63xx_gpio_init, 	.quit = bcm63xx_gpio_quit, };  static struct bitbang_interface bcm63xx_gpio_bitbang = { 	.read = bcm63xx_gpio_read, 	.write = bcm63xx_gpio_write, 	.reset = bcm63xx_gpio_reset, 	.swdio_read = bcm63xx_gpio_swdio_read, 	.swdio_drive = bcm63xx_gpio_swdio_drive, 	.blink = 0 };  static void unusing_all_gpio(void) { 	munmap(pads_base, sysconf(_SC_PAGE_SIZE)); 	close(dev_mem_fd); 	LOG_INFO("unusing_all_gpion"); }  static bool bcm63xx_gpio_jtag_mode_possible(void) { 	if (!is_gpio_valid(tck_gpio)) 		return 0; 	if (!is_gpio_valid(tms_gpio)) 		return 0; 	if (!is_gpio_valid(tdi_gpio)) 		return 0; 	if (!is_gpio_valid(tdo_gpio)) 		return 0; 	return 1; }  static bool bcm63xx_gpio_swd_mode_possible(void) { 	if (!is_gpio_valid(swclk_gpio)) 		return 0; 	if (!is_gpio_valid(swdio_gpio)) 		return 0; 	return 1; }  static int bcm63xx_gpio_init(void) { 	bitbang_interface = &bcm63xx_gpio_bitbang;  	LOG_INFO("bcm63xx_gpio JTAG/SWD bitbang driver");  	if (bcm63xx_gpio_jtag_mode_possible()) { 		if (bcm63xx_gpio_swd_mode_possible()) 			LOG_INFO("JTAG and SWD modes enabled"); 		else 			LOG_INFO("JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)"); 		if (!is_gpio_valid(trst_gpio) && !is_gpio_valid(srst_gpio)) { 			LOG_ERROR("Require at least one of trst or srst gpios to be specified"); 			return ERROR_JTAG_INIT_FAILED; 		} 	} else if (bcm63xx_gpio_swd_mode_possible()) { 		LOG_INFO("SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)"); 	} else { 		LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode and/or swclk and swdio gpio for SWD mode"); 		return ERROR_JTAG_INIT_FAILED; 	}   	/* 	 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST 	 * as outputs.  Drive TDI and TCK low, and TMS/TRST/SRST high. 	 * For SWD, SWCLK and SWDIO are configures as output high. 	 */ 	if (tck_gpio >= 0) { 		tck_fd = setup_bcm63xx_gpio(tck_gpio, 1, 0); 		if (tck_fd < 0) 			goto out_error; 	}  	if (tms_gpio >= 0) { 		tms_fd = setup_bcm63xx_gpio(tms_gpio, 1, 1); 		if (tms_fd < 0) 			goto out_error; 	}  	if (tdi_gpio >= 0) { 		tdi_fd = setup_bcm63xx_gpio(tdi_gpio, 1, 0); 		if (tdi_fd < 0) 			goto out_error; 	}  	if (tdo_gpio >= 0) { 		tdo_fd = setup_bcm63xx_gpio(tdo_gpio, 0, 0); 		if (tdo_fd < 0) 			goto out_error; 	}  	/* assume active low*/ 	if (trst_gpio >= 0) { 		trst_fd = setup_bcm63xx_gpio(trst_gpio, 1, 1); 		if (trst_fd < 0) 			goto out_error; 	}  	/* assume active low*/ 	if (srst_gpio >= 0) { 		srst_fd = setup_bcm63xx_gpio(srst_gpio, 1, 1); 		if (srst_fd < 0) 			goto out_error; 	}  	if (swclk_gpio >= 0) { 		swclk_fd = setup_bcm63xx_gpio(swclk_gpio, 1, 0); 		if (swclk_fd < 0) 			goto out_error; 	}  	if (swdio_gpio >= 0) { 		swdio_fd = setup_bcm63xx_gpio(swdio_gpio, 1, 0); 		if (swdio_fd < 0) 			goto out_error; 	}  	if (bcm63xx_gpio_swd_mode_possible()) { 		if (swd_mode) 			bitbang_swd_switch_seq(JTAG_TO_SWD); 		else 			bitbang_swd_switch_seq(SWD_TO_JTAG); 	}  	return ERROR_OK;  out_error: 	unusing_all_gpio(); 	return ERROR_JTAG_INIT_FAILED; }  static int bcm63xx_gpio_quit(void) { 	unusing_all_gpio(); 	return ERROR_OK; } 

По сравнению с sysfsgpio, добавил пару опций:

  • bcm63xx_gpio_jtag_delay
  • bcm63xx_gpio_adresses

Первая настройка задает задержку между переключениями пинов, является косвенным аналогом bcm2835gpio_speed_coeffs для RaspberryPi драйвера, которая задает частоту работы jtag. К примеру, при нулевой задержке частота переключений была примерно один мегагерц, все работало вполне стабильно, но для надежности лучше иметь возможность задавать этот параметр.

А вторая опция — аналог bcm2835gpio_peripheral_base, только для нее требуется прописать два адреса для регистра, который задает функцию входа/выхода пинов, и регистра, который отвечает за входное/выходное логическое значение на gpio. В начале брал значения регистров из заголовочных файлов ядра. Но с этими значениями ничего не работало. Оказалось, что регистры периферии нельзя напрямую обращаться из userspace, т.е. должен быть сделан ремап еще в ядре. Хорошо, что за меня это уже осуществил gpio драйвер и необходимые значения можно взять из /proc/iomem.

Добавляем наш интерфейс в сборку openocd

Не забываем добавить --enable-bcm63xxgpio к CONFIGURE_ARGS в feeds/packages/utils/openocd/Makefile файле.

Пересобираем, устанавливаем и запускаем на роутере:

root@OpenWrt:~# openocd -f interface/bcm63xx-swd.cfg -f target/stm32f1x.cfg
Open On-Chip Debugger 0.10.0+dev-00085-gfced6ac6-dirty (2017-03-xx-21:49)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
bcm63xx_GPIO num: swclk = 2
bcm63xx_GPIO num: swdio = 11
bcm63xx_GPIO jtag_delay:= 10 tics
bcm63xx_GPIO address: direction = 10000084, value = 1000008c
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
cortex_m reset_config sysresetreq
Info : bcm63xx_gpio JTAG/SWD bitbang driver
Info : SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)
Info : This adapter doesn't support configurable speed
Info : SWD DPIDR 0x1ba01477
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints

Проверяем скорость дампа.

> dump_image dump.bin 0x08000000 0x1ffff dumped 131071 bytes in 4.729815s (27.062 KiB/s) 

Очень даже хорошо, проигрываем в два раза где-то st-link и малинке, но на глаз разница не заметна. Фризов при отладке нет, ну и подождать лишние пару секунд при прошивке — «понты».

Все тесты проводились на микроконтроллере STM32F103C8T6 и только на интерфейсе SWD, на отлаживаемой плате, к сожалению, отсутствовал jtag. Соответственно поэтому гарантировать полноценную работу на jtag не могу. К тому же надо не забывать про согласование уровней сигналов (в частности для MK AVR).

Сам роутер был взят из кучки хлама, среди которого было полно Sagem F@st 2704V2 и V7. К сожалению все устройства были в неисправном состоянии. Но удалось без проблем восстановить плату (см [2]).

Если кто-то готов сделать из этого конструктора отладчик/программатор, то готов на безвозмездной основе поделиться своими запасами с общественностью со снятием с себя всей ответственности и средств на пересылку (из default-city). Для запроса использовать следующий код:

char * my_mail = { 0x20, 0x73, 0x65, 0x72, 0x2d, 0x6d, 0x6b, 0x40, 0x79, 0x61, 0x2e, 0x72, 0x75, 0x0a };

Обновленная прошивка здесь.
Предупреждаю, что изменены стандартные настройки сети и firewall.

На этом всё, удачной отладки!

Список полезных ресурсов

  1. wiki.openwrt.org/toh/sagem/fast2704
  2. radiohlam.ru/forum/viewtopic.php?f=54&t=3749
  3. openocd.org
  4. developer.mbed.org/handbook/CMSIS-DAP
  5. github.com/rogerclarkmelbourne/Arduino_STM32/wiki/Programming-an-STM32F103XXX-with-a-generic-ST-Link-V2-programmer-from-Linux

Источник

bcm63xx, debug, openocd, openwrt, микроконтроллеры

Читайте также