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/jetson-nano-gpio.patch b/jetson-nano-gpio.patch new file mode 100644 index 0000000..cf27638 --- /dev/null +++ b/jetson-nano-gpio.patch @@ -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 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 ++ * Copyright (C) 2022 Jean Biemar ++ * ++ * Jetson nano bitbang driver added by Jean Biemar in 2022 ++ */ ++ ++#include "jetsonNanoJtagBitbang.hpp" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ * Copyright (C) 2022 Jean Biemar ++ * ++ * Jetson nano bitbang driver added by Jean Biemar in 2022 ++ */ ++ ++#ifndef JETSONNANOBITBANG_H ++#define JETSONNANOBITBANG_H ++ ++#include ++ ++#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; 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 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..3965908 --- /dev/null +++ b/src/jetsonNanoJtagBitbang.cpp @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2020-2022 Gwenhael Goavec-Merou + * Copyright (C) 2022 Jean Biemar + * + * Jetson nano bitbang driver added by Jean Biemar in 2022 + */ + +#include "jetsonNanoJtagBitbang.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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..9db5994 --- /dev/null +++ b/src/jetsonNanoJtagBitbang.hpp @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2020-2022 Gwenhael Goavec-Merou + * Copyright (C) 2022 Jean Biemar + * + * Jetson nano bitbang driver added by Jean Biemar in 2022 + */ + +#ifndef JETSONNANOBITBANG_H +#define JETSONNANOBITBANG_H + +#include + +#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;