Add cable jetson-nano-gpio
This commit is contained in:
parent
2d66236533
commit
5a7e73b586
|
|
@ -216,9 +216,16 @@ if (ENABLE_LIBGPIOD)
|
||||||
message("libgpiod support enabled")
|
message("libgpiod support enabled")
|
||||||
endif(ENABLE_LIBGPIOD)
|
endif(ENABLE_LIBGPIOD)
|
||||||
|
|
||||||
if (ENABLE_UDEV OR ENABLE_LIBGPIOD)
|
if (ENABLE_JETSONNANOGPIO)
|
||||||
|
add_definitions(-DENABLE_JETSONNANOGPIO=1)
|
||||||
|
target_sources(openFPGALoader PRIVATE src/jetsonNanoJtagBitbang.cpp)
|
||||||
|
list (APPEND OPENFPGALOADER_HEADERS src/jetsonNanoJtagBitbang.hpp)
|
||||||
|
message("Jetson Nano GPIO support enabled")
|
||||||
|
endif(ENABLE_JETSONNANOGPIO)
|
||||||
|
|
||||||
|
if (ENABLE_UDEV OR ENABLE_LIBGPIOD OR ENABLE_JETSONNANOGPIO)
|
||||||
add_definitions(-DUSE_DEVICE_ARG)
|
add_definitions(-DUSE_DEVICE_ARG)
|
||||||
endif(ENABLE_UDEV OR ENABLE_LIBGPIOD)
|
endif(ENABLE_UDEV OR ENABLE_LIBGPIOD OR ENABLE_JETSONNANOGPIO)
|
||||||
|
|
||||||
if (BUILD_STATIC)
|
if (BUILD_STATIC)
|
||||||
set_target_properties(openFPGALoader PROPERTIES LINK_SEARCH_END_STATIC 1)
|
set_target_properties(openFPGALoader PROPERTIES LINK_SEARCH_END_STATIC 1)
|
||||||
|
|
|
||||||
|
|
@ -257,3 +257,9 @@ libgpiod:
|
||||||
- Name: Bitbang GPIO
|
- Name: Bitbang GPIO
|
||||||
Description: Bitbang GPIO pins on Linux host.
|
Description: Bitbang GPIO pins on Linux host.
|
||||||
URL: https://github.com/brgl/libgpiod
|
URL: https://github.com/brgl/libgpiod
|
||||||
|
|
||||||
|
jetson-nano-gpio:
|
||||||
|
|
||||||
|
- Name: Bitbang GPIO
|
||||||
|
Description: Bitbang GPIO pins on Jetson Nano Linux host. Use /dev/mem to have a faster clock.
|
||||||
|
URL: https://github.com/jwatte/jetson-gpio-example
|
||||||
|
|
@ -0,0 +1,472 @@
|
||||||
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||||
|
index b309fe0..7c6fbe7 100644
|
||||||
|
--- a/CMakeLists.txt
|
||||||
|
+++ b/CMakeLists.txt
|
||||||
|
@@ -216,9 +216,16 @@ if (ENABLE_LIBGPIOD)
|
||||||
|
message("libgpiod support enabled")
|
||||||
|
endif(ENABLE_LIBGPIOD)
|
||||||
|
|
||||||
|
-if (ENABLE_UDEV OR ENABLE_LIBGPIOD)
|
||||||
|
+if (ENABLE_JETSONNANOGPIO)
|
||||||
|
+ add_definitions(-DENABLE_JETSONNANOGPIO=1)
|
||||||
|
+ target_sources(openFPGALoader PRIVATE src/jetsonNanoJtagBitbang.cpp)
|
||||||
|
+ list (APPEND OPENFPGALOADER_HEADERS src/jetsonNanoJtagBitbang.hpp)
|
||||||
|
+ message("Jetson Nano GPIO support enabled")
|
||||||
|
+endif(ENABLE_JETSONNANOGPIO)
|
||||||
|
+
|
||||||
|
+if (ENABLE_UDEV OR ENABLE_LIBGPIOD OR ENABLE_JETSONNANOGPIO)
|
||||||
|
add_definitions(-DUSE_DEVICE_ARG)
|
||||||
|
-endif(ENABLE_UDEV OR ENABLE_LIBGPIOD)
|
||||||
|
+endif(ENABLE_UDEV OR ENABLE_LIBGPIOD OR ENABLE_JETSONNANOGPIO)
|
||||||
|
|
||||||
|
if (BUILD_STATIC)
|
||||||
|
set_target_properties(openFPGALoader PROPERTIES LINK_SEARCH_END_STATIC 1)
|
||||||
|
diff --git a/doc/cable.yml b/doc/cable.yml
|
||||||
|
index 4f62953..019d1c8 100644
|
||||||
|
--- a/doc/cable.yml
|
||||||
|
+++ b/doc/cable.yml
|
||||||
|
@@ -257,3 +257,9 @@ libgpiod:
|
||||||
|
- Name: Bitbang GPIO
|
||||||
|
Description: Bitbang GPIO pins on Linux host.
|
||||||
|
URL: https://github.com/brgl/libgpiod
|
||||||
|
+
|
||||||
|
+jetson-nano-gpio:
|
||||||
|
+
|
||||||
|
+ - Name: Bitbang GPIO
|
||||||
|
+ Description: Bitbang GPIO pins on Jetson Nano Linux host. Use /dev/mem to have a faster clock.
|
||||||
|
+ URL: https://github.com/jwatte/jetson-gpio-example
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/src/cable.hpp b/src/cable.hpp
|
||||||
|
index c383bda..878cba2 100644
|
||||||
|
--- a/src/cable.hpp
|
||||||
|
+++ b/src/cable.hpp
|
||||||
|
@@ -26,6 +26,7 @@ enum communication_type {
|
||||||
|
MODE_DFU, /*! DFU based probe */
|
||||||
|
MODE_XVC_CLIENT, /*! Xilinx Virtual Cable client */
|
||||||
|
MODE_LIBGPIOD_BITBANG, /*! Bitbang gpio pins */
|
||||||
|
+ MODE_JETSONNANO_BITBANG, /*! Bitbang gpio pins */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
@@ -75,6 +76,9 @@ static std::map <std::string, cable_t> cable_list = {
|
||||||
|
#ifdef ENABLE_LIBGPIOD
|
||||||
|
{"libgpiod", {MODE_LIBGPIOD_BITBANG, {}}},
|
||||||
|
#endif
|
||||||
|
+#ifdef ENABLE_JETSONNANOGPIO
|
||||||
|
+ {"jetson-nano-gpio", {MODE_JETSONNANO_BITBANG, {}}},
|
||||||
|
+#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
diff --git a/src/jetsonNanoJtagBitbang.cpp b/src/jetsonNanoJtagBitbang.cpp
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..f16d8de
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/jetsonNanoJtagBitbang.cpp
|
||||||
|
@@ -0,0 +1,269 @@
|
||||||
|
+// SPDX-License-Identifier: Apache-2.0
|
||||||
|
+/*
|
||||||
|
+ * Copyright (C) 2020-2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
+ * Copyright (C) 2022 Jean Biemar <jb@altaneos.com>
|
||||||
|
+ *
|
||||||
|
+ * Jetson nano bitbang driver added by Jean Biemar <jb@altaneos.com> in 2022
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include "jetsonNanoJtagBitbang.hpp"
|
||||||
|
+
|
||||||
|
+#include <stdio.h>
|
||||||
|
+#include <string.h>
|
||||||
|
+
|
||||||
|
+#include <iostream>
|
||||||
|
+#include <map>
|
||||||
|
+#include <vector>
|
||||||
|
+#include <string>
|
||||||
|
+#include <stdexcept>
|
||||||
|
+
|
||||||
|
+#include <unistd.h>
|
||||||
|
+#include <sys/fcntl.h>
|
||||||
|
+#include <sys/mman.h>
|
||||||
|
+
|
||||||
|
+#include "display.hpp"
|
||||||
|
+
|
||||||
|
+#define DEBUG 1
|
||||||
|
+
|
||||||
|
+#ifdef DEBUG
|
||||||
|
+#define display(...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_verbose) fprintf(stdout, __VA_ARGS__); \
|
||||||
|
+ }while(0)
|
||||||
|
+#else
|
||||||
|
+#define display(...) do {}while(0)
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+/* Tegra X1 SoC Technical Reference Manual, version 1.3
|
||||||
|
+ *
|
||||||
|
+ * See Chapter 9 "Multi-Purpose I/O Pins", section 9.13 "GPIO Registers"
|
||||||
|
+ * (table 32: GPIO Register Address Map)
|
||||||
|
+ *
|
||||||
|
+ * The GPIO hardware shares PinMux with up to 4 Special Function I/O per
|
||||||
|
+ * pin, and only one of those five functions (SFIO plus GPIO) can be routed to
|
||||||
|
+ * a pin at a time, using the PixMux.
|
||||||
|
+ *
|
||||||
|
+ * In turn, the PinMux outputs signals to Pads using Pad Control Groups. Pad
|
||||||
|
+ * control groups control things like "drive strength" and "slew rate," and
|
||||||
|
+ * need to be reset after deep sleep. Also, different pads have different
|
||||||
|
+ * voltage tolerance. Pads marked "CZ" can be configured to be 3.3V tolerant
|
||||||
|
+ * and driving; and pads marked "DD" can be 3.3V tolerant when in open-drain
|
||||||
|
+ * mode (only.)
|
||||||
|
+ *
|
||||||
|
+ * The CNF register selects GPIO or SFIO, so setting it to 1 forces the GPIO
|
||||||
|
+ * function. This is convenient for those who have a different pinmux at boot.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#define GPIO_SET_BIT(REG, BIT) REG |= 1UL << BIT
|
||||||
|
+#define GPIO_CLEAR_BIT(REG, BIT) REG &= ~(1UL << BIT)
|
||||||
|
+
|
||||||
|
+JetsonNanoJtagBitbang::JetsonNanoJtagBitbang(
|
||||||
|
+ const jtag_pins_conf_t *pin_conf,
|
||||||
|
+ const std::string &dev, __attribute__((unused)) uint32_t clkHZ,
|
||||||
|
+ uint8_t verbose)
|
||||||
|
+{
|
||||||
|
+ uint32_t tms_port_reg, tck_port_reg, tdi_port_reg, tdo_port_reg;
|
||||||
|
+
|
||||||
|
+ _verbose = verbose;
|
||||||
|
+
|
||||||
|
+ _tck_pin = pin_conf->tck_pin;
|
||||||
|
+ _tms_pin = pin_conf->tms_pin;
|
||||||
|
+ _tdi_pin = pin_conf->tdi_pin;
|
||||||
|
+ _tdo_pin = pin_conf->tdo_pin;
|
||||||
|
+
|
||||||
|
+ display("Jetson Nano jtag bitbang driver, tck_pin=%d, tms_pin=%d, tdi_pin=%d, tdo_pin=%d\n",
|
||||||
|
+ _tck_pin, _tms_pin, _tdi_pin, _tdo_pin);
|
||||||
|
+
|
||||||
|
+ /* Validate pins */
|
||||||
|
+ int pins[] = {_tck_pin, _tms_pin, _tdi_pin, _tdo_pin};
|
||||||
|
+ for (uint32_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
|
||||||
|
+ if (pins[i] < 0 || pins[i] >= 1000) {
|
||||||
|
+ display("Pin %d is outside of valid range\n", pins[i]);
|
||||||
|
+ throw std::runtime_error("A pin is outside of valid range\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (uint32_t j = i + 1; j < sizeof(pins) / sizeof(pins[0]); j++) {
|
||||||
|
+ if (pins[i] == pins[j]) {
|
||||||
|
+ display("Two or more pins are assigned to the same pin number %d\n", pins[i]);
|
||||||
|
+ throw std::runtime_error("Two or more pins are assigned to the same pin number\n");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Get ports */
|
||||||
|
+ tms_port_reg = GPIO_PORT_BASE + ((_tms_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tms_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
+ tck_port_reg = GPIO_PORT_BASE + ((_tck_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tck_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
+ tdi_port_reg = GPIO_PORT_BASE + ((_tdi_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tdi_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
+ tdo_port_reg = GPIO_PORT_BASE + ((_tdo_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tdo_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
+
|
||||||
|
+ /* Get pin */
|
||||||
|
+ _tms_pin %= 8;
|
||||||
|
+ _tck_pin %= 8;
|
||||||
|
+ _tdi_pin %= 8;
|
||||||
|
+ _tdo_pin %= 8;
|
||||||
|
+
|
||||||
|
+ display("reg:pin details: TMS %x:%i, TCK %x:%i, TDI %x:%i, TDO %x:%i\n", tms_port_reg, _tms_pin, tck_port_reg, _tck_pin, tdi_port_reg, _tdi_pin, tdo_port_reg, _tdo_pin);
|
||||||
|
+
|
||||||
|
+ _curr_tdi = 0;
|
||||||
|
+ _curr_tck = 0;
|
||||||
|
+ _curr_tms = 1;
|
||||||
|
+
|
||||||
|
+ // FIXME: I'm unsure how this value should be set.
|
||||||
|
+ // Maybe experiment, or think through what it should be.
|
||||||
|
+ _clkHZ = 5000000;
|
||||||
|
+
|
||||||
|
+ // read physical memory (needs root)
|
||||||
|
+ int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
+ if (fd < 0) {
|
||||||
|
+ display("Can't open /dev/mem. Error %x\n", errno);
|
||||||
|
+ throw std::runtime_error("No access to /dev/mem\n");
|
||||||
|
+ }
|
||||||
|
+ // map a particular physical address into our address space
|
||||||
|
+ int pagesize = getpagesize();
|
||||||
|
+ int pagemask = pagesize-1;
|
||||||
|
+
|
||||||
|
+ // This page will actually contain all the GPIO controllers, because they are co-located
|
||||||
|
+ void *base = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ((tms_port_reg | tck_port_reg | tdi_port_reg | tdo_port_reg) & ~pagemask));
|
||||||
|
+ if (base == NULL) {
|
||||||
|
+ display("mmap return error\n");
|
||||||
|
+ throw std::runtime_error("mmap error\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _tms_port = (gpio_t volatile *)((char *)base + (tms_port_reg & pagemask));
|
||||||
|
+ _tck_port = (gpio_t volatile *)((char *)base + (tck_port_reg & pagemask));
|
||||||
|
+ _tdi_port = (gpio_t volatile *)((char *)base + (tdi_port_reg & pagemask));
|
||||||
|
+ _tdo_port = (gpio_t volatile *)((char *)base + (tdo_port_reg & pagemask));
|
||||||
|
+
|
||||||
|
+ // for _tms_port : GPIO OUT
|
||||||
|
+ GPIO_SET_BIT(_tms_port->CNF, _tms_pin);
|
||||||
|
+ GPIO_SET_BIT(_tms_port->OE, _tms_pin);
|
||||||
|
+ GPIO_SET_BIT(_tms_port->OUT, _tms_pin);
|
||||||
|
+
|
||||||
|
+ // for _tck_port : GPIO OUT
|
||||||
|
+ GPIO_SET_BIT(_tck_port->CNF, _tck_pin);
|
||||||
|
+ GPIO_SET_BIT(_tck_port->OE, _tck_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tck_port->OUT, _tck_pin);
|
||||||
|
+
|
||||||
|
+ // for _tdi_port : GPIO OUT
|
||||||
|
+ GPIO_SET_BIT(_tdi_port->CNF, _tdi_pin);
|
||||||
|
+ GPIO_SET_BIT(_tdi_port->OE, _tdi_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tdi_port->OUT, _tdi_pin);
|
||||||
|
+
|
||||||
|
+ // for _tdo_port : GPIO IN
|
||||||
|
+ GPIO_SET_BIT(_tdo_port->CNF, _tdo_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tdo_port->OE , _tdo_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tdo_port->IN, _tdo_pin);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+JetsonNanoJtagBitbang::~JetsonNanoJtagBitbang()
|
||||||
|
+{
|
||||||
|
+ GPIO_CLEAR_BIT(_tms_port->OE, _tms_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tck_port->OE, _tck_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tdi_port->OE, _tdi_pin);
|
||||||
|
+
|
||||||
|
+ GPIO_CLEAR_BIT(_tms_port->CNF, _tms_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tck_port->CNF, _tck_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tdi_port->CNF, _tdi_pin);
|
||||||
|
+ GPIO_CLEAR_BIT(_tdo_port->CNF, _tdo_pin);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int JetsonNanoJtagBitbang::update_pins(int tck, int tms, int tdi)
|
||||||
|
+{
|
||||||
|
+ if (tdi != _curr_tdi) {
|
||||||
|
+ if(tdi)
|
||||||
|
+ GPIO_SET_BIT(_tdi_port->OUT, _tdi_pin);
|
||||||
|
+ else
|
||||||
|
+ GPIO_CLEAR_BIT(_tdi_port->OUT, _tdi_pin);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (tms != _curr_tms) {
|
||||||
|
+ if(tms)
|
||||||
|
+ GPIO_SET_BIT(_tms_port->OUT, _tms_pin);
|
||||||
|
+ else
|
||||||
|
+ GPIO_CLEAR_BIT(_tms_port->OUT, _tms_pin);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (tck != _curr_tck) {
|
||||||
|
+ if(tck)
|
||||||
|
+ GPIO_SET_BIT(_tck_port->OUT, _tck_pin);
|
||||||
|
+ else
|
||||||
|
+ GPIO_CLEAR_BIT(_tck_port->OUT, _tck_pin);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _curr_tdi = tdi;
|
||||||
|
+ _curr_tms = tms;
|
||||||
|
+ _curr_tck = tck;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int JetsonNanoJtagBitbang::read_tdo()
|
||||||
|
+{
|
||||||
|
+ return (_tdo_port->IN>>_tdo_pin) & 0x01;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int JetsonNanoJtagBitbang::setClkFreq(__attribute__((unused)) uint32_t clkHZ)
|
||||||
|
+{
|
||||||
|
+ // FIXME: The assumption is that calling the gpiod_line_set_value
|
||||||
|
+ // routine will limit the clock frequency to lower than what is specified.
|
||||||
|
+ // This needs to be verified, and possibly artificial delays should be added.
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int JetsonNanoJtagBitbang::writeTMS(uint8_t *tms_buf, uint32_t len,
|
||||||
|
+ __attribute__((unused)) bool flush_buffer)
|
||||||
|
+{
|
||||||
|
+ int tms;
|
||||||
|
+
|
||||||
|
+ for (uint32_t i = 0; i < len; i++) {
|
||||||
|
+ tms = ((tms_buf[i >> 3] & (1 << (i & 7))) ? 1 : 0);
|
||||||
|
+
|
||||||
|
+ update_pins(0, tms, 0);
|
||||||
|
+ update_pins(1, tms, 0);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ update_pins(0, tms, 0);
|
||||||
|
+
|
||||||
|
+ return len;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int JetsonNanoJtagBitbang::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
|
+{
|
||||||
|
+ int tms = _curr_tms;
|
||||||
|
+ int tdi = _curr_tdi;
|
||||||
|
+
|
||||||
|
+ if (rx)
|
||||||
|
+ memset(rx, 0, len / 8);
|
||||||
|
+
|
||||||
|
+ for (uint32_t i = 0; i < len; i++) {
|
||||||
|
+ if (end && (i == len - 1))
|
||||||
|
+ tms = 1;
|
||||||
|
+
|
||||||
|
+ if (tx)
|
||||||
|
+ tdi = (tx[i >> 3] & (1 << (i & 7))) ? 1 : 0;
|
||||||
|
+
|
||||||
|
+ update_pins(0, tms, tdi);
|
||||||
|
+ update_pins(1, tms, tdi);
|
||||||
|
+
|
||||||
|
+ if (rx) {
|
||||||
|
+ if (read_tdo() > 0)
|
||||||
|
+ rx[i >> 3] |= 1 << (i & 7);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ update_pins(0, tms, tdi);
|
||||||
|
+
|
||||||
|
+ return len;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int JetsonNanoJtagBitbang::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
||||||
|
+{
|
||||||
|
+ for (uint32_t i = 0; i < clk_len; i++) {
|
||||||
|
+ update_pins(0, tms, tdi);
|
||||||
|
+ update_pins(1, tms, tdi);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ update_pins(0, tms, tdi);
|
||||||
|
+
|
||||||
|
+ return clk_len;
|
||||||
|
+}
|
||||||
|
diff --git a/src/jetsonNanoJtagBitbang.hpp b/src/jetsonNanoJtagBitbang.hpp
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..8c1f443
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/jetsonNanoJtagBitbang.hpp
|
||||||
|
@@ -0,0 +1,105 @@
|
||||||
|
+// SPDX-License-Identifier: Apache-2.0
|
||||||
|
+/*
|
||||||
|
+ * Copyright (C) 2020-2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
+ * Copyright (C) 2022 Jean Biemar <jb@altaneos.com>
|
||||||
|
+ *
|
||||||
|
+ * Jetson nano bitbang driver added by Jean Biemar <jb@altaneos.com> in 2022
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef JETSONNANOBITBANG_H
|
||||||
|
+#define JETSONNANOBITBANG_H
|
||||||
|
+
|
||||||
|
+#include <string>
|
||||||
|
+
|
||||||
|
+#include "board.hpp"
|
||||||
|
+#include "jtagInterface.hpp"
|
||||||
|
+
|
||||||
|
+/* Tegra X1 SoC Technical Reference Manual, version 1.3
|
||||||
|
+ *
|
||||||
|
+ * See Chapter 9 "Multi-Purpose I/O Pins", section 9.13 "GPIO Registers"
|
||||||
|
+ * (table 32: GPIO Register Address Map)
|
||||||
|
+ *
|
||||||
|
+ * The GPIO hardware shares PinMux with up to 4 Special Function I/O per
|
||||||
|
+ * pin, and only one of those five functions (SFIO plus GPIO) can be routed to
|
||||||
|
+ * a pin at a time, using the PixMux.
|
||||||
|
+ *
|
||||||
|
+ * In turn, the PinMux outputs signals to Pads using Pad Control Groups. Pad
|
||||||
|
+ * control groups control things like "drive strength" and "slew rate," and
|
||||||
|
+ * need to be reset after deep sleep. Also, different pads have different
|
||||||
|
+ * voltage tolerance. Pads marked "CZ" can be configured to be 3.3V tolerant
|
||||||
|
+ * and driving; and pads marked "DD" can be 3.3V tolerant when in open-drain
|
||||||
|
+ * mode (only.)
|
||||||
|
+ *
|
||||||
|
+ * The CNF register selects GPIO or SFIO, so setting it to 1 forces the GPIO
|
||||||
|
+ * function. This is convenient for those who have a different pinmux at boot.
|
||||||
|
+ */
|
||||||
|
+#define GPIO_PORT_BASE 0x6000d000 // Port A
|
||||||
|
+#define GPIO_PORT_MAX 0x6000d708 // Port EE
|
||||||
|
+#define GPIO_PORT_SHORT_OFFSET_SIZE 4
|
||||||
|
+#define GPIO_PORT_SHORT_OFFSET 0x00000004
|
||||||
|
+#define GPIO_PORT_LARGE_OFFSET 0x00000100
|
||||||
|
+
|
||||||
|
+/*!
|
||||||
|
+ * \file JetsonNanoJtagBitbang.hpp
|
||||||
|
+ * \class JetsonNanoJtagBitbang
|
||||||
|
+ * \brief concrete class between jtag implementation and gpio bitbang
|
||||||
|
+ * \author Jean Biemar
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+class JetsonNanoJtagBitbang : public JtagInterface {
|
||||||
|
+ public:
|
||||||
|
+ JetsonNanoJtagBitbang(const jtag_pins_conf_t *pin_conf,
|
||||||
|
+ const std::string &dev, uint32_t clkHZ, uint8_t verbose);
|
||||||
|
+ virtual ~JetsonNanoJtagBitbang();
|
||||||
|
+
|
||||||
|
+ int setClkFreq(uint32_t clkHZ) override;
|
||||||
|
+ int writeTMS(uint8_t *tms_buf, uint32_t len, bool flush_buffer) override;
|
||||||
|
+ int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override;
|
||||||
|
+ int toggleClk(uint8_t tms, uint8_t tdo, uint32_t clk_len) override;
|
||||||
|
+
|
||||||
|
+ int get_buffer_size() override { return 0; }
|
||||||
|
+ bool isFull() override { return false; }
|
||||||
|
+ int flush() override { return 0; }
|
||||||
|
+
|
||||||
|
+ private:
|
||||||
|
+ // layout based on the definitions above
|
||||||
|
+ // Each GPIO controller has four ports, each port controls 8 pins, each
|
||||||
|
+ // register is interleaved for the four ports, so
|
||||||
|
+ // REGX: port0, port1, port2, port3
|
||||||
|
+ // REGY: port0, port1, port2, port3
|
||||||
|
+ typedef struct {
|
||||||
|
+ uint32_t CNF;
|
||||||
|
+ uint32_t _padding1[3];
|
||||||
|
+ uint32_t OE;
|
||||||
|
+ uint32_t _padding2[3];
|
||||||
|
+ uint32_t OUT;
|
||||||
|
+ uint32_t _padding3[3];
|
||||||
|
+ uint32_t IN;
|
||||||
|
+ uint32_t _padding4[3];
|
||||||
|
+ uint32_t INT_STA;
|
||||||
|
+ uint32_t _padding5[3];
|
||||||
|
+ uint32_t INT_ENB;
|
||||||
|
+ uint32_t _padding6[3];
|
||||||
|
+ uint32_t INT_LVL;
|
||||||
|
+ uint32_t _padding7[3];
|
||||||
|
+ uint32_t INT_CLR;
|
||||||
|
+ uint32_t _padding8[3];
|
||||||
|
+ } gpio_t;
|
||||||
|
+
|
||||||
|
+ int update_pins(int tck, int tms, int tdi);
|
||||||
|
+ int read_tdo();
|
||||||
|
+
|
||||||
|
+ bool _verbose;
|
||||||
|
+
|
||||||
|
+ int _tck_pin;
|
||||||
|
+ int _tms_pin;
|
||||||
|
+ int _tdo_pin;
|
||||||
|
+ int _tdi_pin;
|
||||||
|
+
|
||||||
|
+ gpio_t volatile *_tms_port, *_tck_port, *_tdo_port, *_tdi_port;
|
||||||
|
+
|
||||||
|
+ int _curr_tms;
|
||||||
|
+ int _curr_tdi;
|
||||||
|
+ int _curr_tck;
|
||||||
|
+};
|
||||||
|
+#endif
|
||||||
|
diff --git a/src/jtag.cpp b/src/jtag.cpp
|
||||||
|
index 4bd6f72..6e23ee6 100644
|
||||||
|
--- a/src/jtag.cpp
|
||||||
|
+++ b/src/jtag.cpp
|
||||||
|
@@ -24,6 +24,9 @@
|
||||||
|
#ifdef ENABLE_LIBGPIOD
|
||||||
|
#include "libgpiodJtagBitbang.hpp"
|
||||||
|
#endif
|
||||||
|
+#ifdef ENABLE_JETSONNANOGPIO
|
||||||
|
+#include "jetsonNanoJtagBitbang.hpp"
|
||||||
|
+#endif
|
||||||
|
#include "jlink.hpp"
|
||||||
|
#ifdef ENABLE_CMSISDAP
|
||||||
|
#include "cmsisDAP.hpp"
|
||||||
|
@@ -139,6 +142,11 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const string &serial
|
||||||
|
case MODE_LIBGPIOD_BITBANG:
|
||||||
|
_jtag = new LibgpiodJtagBitbang(pin_conf, dev, clkHZ, _verbose);
|
||||||
|
break;
|
||||||
|
+#endif
|
||||||
|
+#ifdef ENABLE_JETSONNANOGPIO
|
||||||
|
+ case MODE_JETSONNANO_BITBANG:
|
||||||
|
+ _jtag = new JetsonNanoJtagBitbang(pin_conf, dev, clkHZ, _verbose);
|
||||||
|
+ break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
std::cerr << "Jtag: unknown cable type" << std::endl;
|
||||||
|
|
@ -26,6 +26,7 @@ enum communication_type {
|
||||||
MODE_DFU, /*! DFU based probe */
|
MODE_DFU, /*! DFU based probe */
|
||||||
MODE_XVC_CLIENT, /*! Xilinx Virtual Cable client */
|
MODE_XVC_CLIENT, /*! Xilinx Virtual Cable client */
|
||||||
MODE_LIBGPIOD_BITBANG, /*! Bitbang gpio pins */
|
MODE_LIBGPIOD_BITBANG, /*! Bitbang gpio pins */
|
||||||
|
MODE_JETSONNANO_BITBANG, /*! Bitbang gpio pins */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -75,6 +76,9 @@ static std::map <std::string, cable_t> cable_list = {
|
||||||
#ifdef ENABLE_LIBGPIOD
|
#ifdef ENABLE_LIBGPIOD
|
||||||
{"libgpiod", {MODE_LIBGPIOD_BITBANG, {}}},
|
{"libgpiod", {MODE_LIBGPIOD_BITBANG, {}}},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_JETSONNANOGPIO
|
||||||
|
{"jetson-nano-gpio", {MODE_JETSONNANO_BITBANG, {}}},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
* Copyright (C) 2022 Jean Biemar <jb@altaneos.com>
|
||||||
|
*
|
||||||
|
* Jetson nano bitbang driver added by Jean Biemar <jb@altaneos.com> in 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jetsonNanoJtagBitbang.hpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include "display.hpp"
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define display(...) \
|
||||||
|
do { \
|
||||||
|
if (_verbose) fprintf(stdout, __VA_ARGS__); \
|
||||||
|
}while(0)
|
||||||
|
#else
|
||||||
|
#define display(...) do {}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Tegra X1 SoC Technical Reference Manual, version 1.3
|
||||||
|
*
|
||||||
|
* See Chapter 9 "Multi-Purpose I/O Pins", section 9.13 "GPIO Registers"
|
||||||
|
* (table 32: GPIO Register Address Map)
|
||||||
|
*
|
||||||
|
* The GPIO hardware shares PinMux with up to 4 Special Function I/O per
|
||||||
|
* pin, and only one of those five functions (SFIO plus GPIO) can be routed to
|
||||||
|
* a pin at a time, using the PixMux.
|
||||||
|
*
|
||||||
|
* In turn, the PinMux outputs signals to Pads using Pad Control Groups. Pad
|
||||||
|
* control groups control things like "drive strength" and "slew rate," and
|
||||||
|
* need to be reset after deep sleep. Also, different pads have different
|
||||||
|
* voltage tolerance. Pads marked "CZ" can be configured to be 3.3V tolerant
|
||||||
|
* and driving; and pads marked "DD" can be 3.3V tolerant when in open-drain
|
||||||
|
* mode (only.)
|
||||||
|
*
|
||||||
|
* The CNF register selects GPIO or SFIO, so setting it to 1 forces the GPIO
|
||||||
|
* function. This is convenient for those who have a different pinmux at boot.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define GPIO_SET_BIT(REG, BIT) REG |= 1UL << BIT
|
||||||
|
#define GPIO_CLEAR_BIT(REG, BIT) REG &= ~(1UL << BIT)
|
||||||
|
|
||||||
|
JetsonNanoJtagBitbang::JetsonNanoJtagBitbang(
|
||||||
|
const jtag_pins_conf_t *pin_conf,
|
||||||
|
const std::string &dev, __attribute__((unused)) uint32_t clkHZ,
|
||||||
|
uint8_t verbose)
|
||||||
|
{
|
||||||
|
uint32_t tms_port_reg, tck_port_reg, tdi_port_reg, tdo_port_reg;
|
||||||
|
|
||||||
|
_verbose = verbose;
|
||||||
|
|
||||||
|
_tck_pin = pin_conf->tck_pin;
|
||||||
|
_tms_pin = pin_conf->tms_pin;
|
||||||
|
_tdi_pin = pin_conf->tdi_pin;
|
||||||
|
_tdo_pin = pin_conf->tdo_pin;
|
||||||
|
|
||||||
|
display("Jetson Nano jtag bitbang driver, tck_pin=%d, tms_pin=%d, tdi_pin=%d, tdo_pin=%d\n",
|
||||||
|
_tck_pin, _tms_pin, _tdi_pin, _tdo_pin);
|
||||||
|
|
||||||
|
/* Validate pins */
|
||||||
|
int pins[] = {_tck_pin, _tms_pin, _tdi_pin, _tdo_pin};
|
||||||
|
for (uint32_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
|
||||||
|
if (pins[i] < 0 || pins[i] >= 1000) {
|
||||||
|
display("Pin %d is outside of valid range\n", pins[i]);
|
||||||
|
throw std::runtime_error("A pin is outside of valid range\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t j = i + 1; j < sizeof(pins) / sizeof(pins[0]); j++) {
|
||||||
|
if (pins[i] == pins[j]) {
|
||||||
|
display("Two or more pins are assigned to the same pin number %d\n", pins[i]);
|
||||||
|
throw std::runtime_error("Two or more pins are assigned to the same pin number\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get ports */
|
||||||
|
tms_port_reg = GPIO_PORT_BASE + ((_tms_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tms_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
tck_port_reg = GPIO_PORT_BASE + ((_tck_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tck_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
tdi_port_reg = GPIO_PORT_BASE + ((_tdi_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tdi_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
tdo_port_reg = GPIO_PORT_BASE + ((_tdo_pin/8)/GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_LARGE_OFFSET) + ((_tdo_pin/8)%GPIO_PORT_SHORT_OFFSET_SIZE*GPIO_PORT_SHORT_OFFSET);
|
||||||
|
|
||||||
|
/* Get pin */
|
||||||
|
_tms_pin %= 8;
|
||||||
|
_tck_pin %= 8;
|
||||||
|
_tdi_pin %= 8;
|
||||||
|
_tdo_pin %= 8;
|
||||||
|
|
||||||
|
display("reg:pin details: TMS %x:%i, TCK %x:%i, TDI %x:%i, TDO %x:%i\n", tms_port_reg, _tms_pin, tck_port_reg, _tck_pin, tdi_port_reg, _tdi_pin, tdo_port_reg, _tdo_pin);
|
||||||
|
|
||||||
|
_curr_tdi = 0;
|
||||||
|
_curr_tck = 0;
|
||||||
|
_curr_tms = 1;
|
||||||
|
|
||||||
|
// FIXME: I'm unsure how this value should be set.
|
||||||
|
// Maybe experiment, or think through what it should be.
|
||||||
|
_clkHZ = 5000000;
|
||||||
|
|
||||||
|
// read physical memory (needs root)
|
||||||
|
int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
if (fd < 0) {
|
||||||
|
display("Can't open /dev/mem. Error %x\n", errno);
|
||||||
|
throw std::runtime_error("No access to /dev/mem\n");
|
||||||
|
}
|
||||||
|
// map a particular physical address into our address space
|
||||||
|
int pagesize = getpagesize();
|
||||||
|
int pagemask = pagesize-1;
|
||||||
|
|
||||||
|
// This page will actually contain all the GPIO controllers, because they are co-located
|
||||||
|
void *base = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ((tms_port_reg | tck_port_reg | tdi_port_reg | tdo_port_reg) & ~pagemask));
|
||||||
|
if (base == NULL) {
|
||||||
|
display("mmap return error\n");
|
||||||
|
throw std::runtime_error("mmap error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
_tms_port = (gpio_t volatile *)((char *)base + (tms_port_reg & pagemask));
|
||||||
|
_tck_port = (gpio_t volatile *)((char *)base + (tck_port_reg & pagemask));
|
||||||
|
_tdi_port = (gpio_t volatile *)((char *)base + (tdi_port_reg & pagemask));
|
||||||
|
_tdo_port = (gpio_t volatile *)((char *)base + (tdo_port_reg & pagemask));
|
||||||
|
|
||||||
|
// for _tms_port : GPIO OUT
|
||||||
|
GPIO_SET_BIT(_tms_port->CNF, _tms_pin);
|
||||||
|
GPIO_SET_BIT(_tms_port->OE, _tms_pin);
|
||||||
|
GPIO_SET_BIT(_tms_port->OUT, _tms_pin);
|
||||||
|
|
||||||
|
// for _tck_port : GPIO OUT
|
||||||
|
GPIO_SET_BIT(_tck_port->CNF, _tck_pin);
|
||||||
|
GPIO_SET_BIT(_tck_port->OE, _tck_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tck_port->OUT, _tck_pin);
|
||||||
|
|
||||||
|
// for _tdi_port : GPIO OUT
|
||||||
|
GPIO_SET_BIT(_tdi_port->CNF, _tdi_pin);
|
||||||
|
GPIO_SET_BIT(_tdi_port->OE, _tdi_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tdi_port->OUT, _tdi_pin);
|
||||||
|
|
||||||
|
// for _tdo_port : GPIO IN
|
||||||
|
GPIO_SET_BIT(_tdo_port->CNF, _tdo_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tdo_port->OE , _tdo_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tdo_port->IN, _tdo_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
JetsonNanoJtagBitbang::~JetsonNanoJtagBitbang()
|
||||||
|
{
|
||||||
|
GPIO_CLEAR_BIT(_tms_port->OE, _tms_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tck_port->OE, _tck_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tdi_port->OE, _tdi_pin);
|
||||||
|
|
||||||
|
GPIO_CLEAR_BIT(_tms_port->CNF, _tms_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tck_port->CNF, _tck_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tdi_port->CNF, _tdi_pin);
|
||||||
|
GPIO_CLEAR_BIT(_tdo_port->CNF, _tdo_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
int JetsonNanoJtagBitbang::update_pins(int tck, int tms, int tdi)
|
||||||
|
{
|
||||||
|
if (tdi != _curr_tdi) {
|
||||||
|
if(tdi)
|
||||||
|
GPIO_SET_BIT(_tdi_port->OUT, _tdi_pin);
|
||||||
|
else
|
||||||
|
GPIO_CLEAR_BIT(_tdi_port->OUT, _tdi_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tms != _curr_tms) {
|
||||||
|
if(tms)
|
||||||
|
GPIO_SET_BIT(_tms_port->OUT, _tms_pin);
|
||||||
|
else
|
||||||
|
GPIO_CLEAR_BIT(_tms_port->OUT, _tms_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tck != _curr_tck) {
|
||||||
|
if(tck)
|
||||||
|
GPIO_SET_BIT(_tck_port->OUT, _tck_pin);
|
||||||
|
else
|
||||||
|
GPIO_CLEAR_BIT(_tck_port->OUT, _tck_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
_curr_tdi = tdi;
|
||||||
|
_curr_tms = tms;
|
||||||
|
_curr_tck = tck;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JetsonNanoJtagBitbang::read_tdo()
|
||||||
|
{
|
||||||
|
return (_tdo_port->IN>>_tdo_pin) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JetsonNanoJtagBitbang::setClkFreq(__attribute__((unused)) uint32_t clkHZ)
|
||||||
|
{
|
||||||
|
// FIXME: The assumption is that calling the gpiod_line_set_value
|
||||||
|
// routine will limit the clock frequency to lower than what is specified.
|
||||||
|
// This needs to be verified, and possibly artificial delays should be added.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JetsonNanoJtagBitbang::writeTMS(uint8_t *tms_buf, uint32_t len,
|
||||||
|
__attribute__((unused)) bool flush_buffer)
|
||||||
|
{
|
||||||
|
int tms;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < len; i++) {
|
||||||
|
tms = ((tms_buf[i >> 3] & (1 << (i & 7))) ? 1 : 0);
|
||||||
|
|
||||||
|
update_pins(0, tms, 0);
|
||||||
|
update_pins(1, tms, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_pins(0, tms, 0);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JetsonNanoJtagBitbang::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
|
{
|
||||||
|
int tms = _curr_tms;
|
||||||
|
int tdi = _curr_tdi;
|
||||||
|
|
||||||
|
if (rx)
|
||||||
|
memset(rx, 0, len / 8);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < len; i++) {
|
||||||
|
if (end && (i == len - 1))
|
||||||
|
tms = 1;
|
||||||
|
|
||||||
|
if (tx)
|
||||||
|
tdi = (tx[i >> 3] & (1 << (i & 7))) ? 1 : 0;
|
||||||
|
|
||||||
|
update_pins(0, tms, tdi);
|
||||||
|
update_pins(1, tms, tdi);
|
||||||
|
|
||||||
|
if (rx) {
|
||||||
|
if (read_tdo() > 0)
|
||||||
|
rx[i >> 3] |= 1 << (i & 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_pins(0, tms, tdi);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JetsonNanoJtagBitbang::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < clk_len; i++) {
|
||||||
|
update_pins(0, tms, tdi);
|
||||||
|
update_pins(1, tms, tdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_pins(0, tms, tdi);
|
||||||
|
|
||||||
|
return clk_len;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
* Copyright (C) 2022 Jean Biemar <jb@altaneos.com>
|
||||||
|
*
|
||||||
|
* Jetson nano bitbang driver added by Jean Biemar <jb@altaneos.com> in 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JETSONNANOBITBANG_H
|
||||||
|
#define JETSONNANOBITBANG_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "board.hpp"
|
||||||
|
#include "jtagInterface.hpp"
|
||||||
|
|
||||||
|
/* Tegra X1 SoC Technical Reference Manual, version 1.3
|
||||||
|
*
|
||||||
|
* See Chapter 9 "Multi-Purpose I/O Pins", section 9.13 "GPIO Registers"
|
||||||
|
* (table 32: GPIO Register Address Map)
|
||||||
|
*
|
||||||
|
* The GPIO hardware shares PinMux with up to 4 Special Function I/O per
|
||||||
|
* pin, and only one of those five functions (SFIO plus GPIO) can be routed to
|
||||||
|
* a pin at a time, using the PixMux.
|
||||||
|
*
|
||||||
|
* In turn, the PinMux outputs signals to Pads using Pad Control Groups. Pad
|
||||||
|
* control groups control things like "drive strength" and "slew rate," and
|
||||||
|
* need to be reset after deep sleep. Also, different pads have different
|
||||||
|
* voltage tolerance. Pads marked "CZ" can be configured to be 3.3V tolerant
|
||||||
|
* and driving; and pads marked "DD" can be 3.3V tolerant when in open-drain
|
||||||
|
* mode (only.)
|
||||||
|
*
|
||||||
|
* The CNF register selects GPIO or SFIO, so setting it to 1 forces the GPIO
|
||||||
|
* function. This is convenient for those who have a different pinmux at boot.
|
||||||
|
*/
|
||||||
|
#define GPIO_PORT_BASE 0x6000d000 // Port A
|
||||||
|
#define GPIO_PORT_MAX 0x6000d708 // Port EE
|
||||||
|
#define GPIO_PORT_SHORT_OFFSET_SIZE 4
|
||||||
|
#define GPIO_PORT_SHORT_OFFSET 0x00000004
|
||||||
|
#define GPIO_PORT_LARGE_OFFSET 0x00000100
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file JetsonNanoJtagBitbang.hpp
|
||||||
|
* \class JetsonNanoJtagBitbang
|
||||||
|
* \brief concrete class between jtag implementation and gpio bitbang
|
||||||
|
* \author Jean Biemar
|
||||||
|
*/
|
||||||
|
|
||||||
|
class JetsonNanoJtagBitbang : public JtagInterface {
|
||||||
|
public:
|
||||||
|
JetsonNanoJtagBitbang(const jtag_pins_conf_t *pin_conf,
|
||||||
|
const std::string &dev, uint32_t clkHZ, uint8_t verbose);
|
||||||
|
virtual ~JetsonNanoJtagBitbang();
|
||||||
|
|
||||||
|
int setClkFreq(uint32_t clkHZ) override;
|
||||||
|
int writeTMS(uint8_t *tms_buf, uint32_t len, bool flush_buffer) override;
|
||||||
|
int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override;
|
||||||
|
int toggleClk(uint8_t tms, uint8_t tdo, uint32_t clk_len) override;
|
||||||
|
|
||||||
|
int get_buffer_size() override { return 0; }
|
||||||
|
bool isFull() override { return false; }
|
||||||
|
int flush() override { return 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// layout based on the definitions above
|
||||||
|
// Each GPIO controller has four ports, each port controls 8 pins, each
|
||||||
|
// register is interleaved for the four ports, so
|
||||||
|
// REGX: port0, port1, port2, port3
|
||||||
|
// REGY: port0, port1, port2, port3
|
||||||
|
typedef struct {
|
||||||
|
uint32_t CNF;
|
||||||
|
uint32_t _padding1[3];
|
||||||
|
uint32_t OE;
|
||||||
|
uint32_t _padding2[3];
|
||||||
|
uint32_t OUT;
|
||||||
|
uint32_t _padding3[3];
|
||||||
|
uint32_t IN;
|
||||||
|
uint32_t _padding4[3];
|
||||||
|
uint32_t INT_STA;
|
||||||
|
uint32_t _padding5[3];
|
||||||
|
uint32_t INT_ENB;
|
||||||
|
uint32_t _padding6[3];
|
||||||
|
uint32_t INT_LVL;
|
||||||
|
uint32_t _padding7[3];
|
||||||
|
uint32_t INT_CLR;
|
||||||
|
uint32_t _padding8[3];
|
||||||
|
} gpio_t;
|
||||||
|
|
||||||
|
int update_pins(int tck, int tms, int tdi);
|
||||||
|
int read_tdo();
|
||||||
|
|
||||||
|
bool _verbose;
|
||||||
|
|
||||||
|
int _tck_pin;
|
||||||
|
int _tms_pin;
|
||||||
|
int _tdo_pin;
|
||||||
|
int _tdi_pin;
|
||||||
|
|
||||||
|
gpio_t volatile *_tms_port, *_tck_port, *_tdo_port, *_tdi_port;
|
||||||
|
|
||||||
|
int _curr_tms;
|
||||||
|
int _curr_tdi;
|
||||||
|
int _curr_tck;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
@ -24,6 +24,9 @@
|
||||||
#ifdef ENABLE_LIBGPIOD
|
#ifdef ENABLE_LIBGPIOD
|
||||||
#include "libgpiodJtagBitbang.hpp"
|
#include "libgpiodJtagBitbang.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_JETSONNANOGPIO
|
||||||
|
#include "jetsonNanoJtagBitbang.hpp"
|
||||||
|
#endif
|
||||||
#include "jlink.hpp"
|
#include "jlink.hpp"
|
||||||
#ifdef ENABLE_CMSISDAP
|
#ifdef ENABLE_CMSISDAP
|
||||||
#include "cmsisDAP.hpp"
|
#include "cmsisDAP.hpp"
|
||||||
|
|
@ -139,6 +142,11 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const string &serial
|
||||||
case MODE_LIBGPIOD_BITBANG:
|
case MODE_LIBGPIOD_BITBANG:
|
||||||
_jtag = new LibgpiodJtagBitbang(pin_conf, dev, clkHZ, _verbose);
|
_jtag = new LibgpiodJtagBitbang(pin_conf, dev, clkHZ, _verbose);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_JETSONNANOGPIO
|
||||||
|
case MODE_JETSONNANO_BITBANG:
|
||||||
|
_jtag = new JetsonNanoJtagBitbang(pin_conf, dev, clkHZ, _verbose);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
std::cerr << "Jtag: unknown cable type" << std::endl;
|
std::cerr << "Jtag: unknown cable type" << std::endl;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue