Merge pull request #260 from jeanbie0/jetson-nano-gpio

Add cable jetson-nano-gpio
This commit is contained in:
Gwenhael Goavec-Merou 2022-10-22 15:07:00 +02:00 committed by GitHub
commit 106cdecc14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 401 additions and 2 deletions

View File

@ -218,9 +218,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)

View File

@ -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

View File

@ -24,6 +24,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 */
};
/*!
@ -115,6 +116,9 @@ static std::map <std::string, cable_t> cable_list = {
#ifdef ENABLE_LIBGPIOD
{"libgpiod", CABLE_DEF(MODE_LIBGPIOD_BITBANG, 0, 0x0000 )},
#endif
#ifdef ENABLE_JETSONNANOGPIO
{"jetson-nano-gpio", {MODE_JETSONNANO_BITBANG, {}}},
#endif
};
#endif // SRC_CABLE_HPP_

View File

@ -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;
}

View File

@ -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

View File

@ -20,6 +20,9 @@
#ifdef ENABLE_LIBGPIOD
#include "libgpiodJtagBitbang.hpp"
#endif
#ifdef ENABLE_JETSONNANOGPIO
#include "jetsonNanoJtagBitbang.hpp"
#endif
#include "jlink.hpp"
#ifdef ENABLE_CMSISDAP
#include "cmsisDAP.hpp"
@ -133,6 +136,11 @@ void Jtag::init_internal(const cable_t &cable, const string &dev, const string &
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;