Last active
July 15, 2022 06:58
-
-
Save trabucayre/2f31c0b46bd4d95af1287d4e6eecd3e6 to your computer and use it in GitHub Desktop.
libgpiod wrapper for JTAG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <gpiod.h> | |
| #include <cstring> | |
| #include <stdexcept> | |
| /*! | |
| * \brief LibGPIOD driver | |
| */ | |
| class MyGPIOD { | |
| public: | |
| /*! | |
| * \brief constructor: open a device and configure JTAG pins | |
| * \param[in] devname: gpiochip peripheral (/dev/gpiochipX) | |
| * \param[in] tms_off: tms pin ID | |
| * \param[in] tck_off: tck pin ID | |
| * \param[in] tdo_off: tdo pin ID | |
| * \param[in] tdi_off: tdi pin ID | |
| */ | |
| MyGPIOD(const char *devname, uint32_t tms_off, uint32_t tck_off, | |
| uint32_t tdo_off, uint32_t tdi_off):_devname(devname), | |
| _tms(tms_off), _tck(tck_off), _tdo(tdo_off), _tdi(tdi_off), | |
| _chip(NULL), _lines(GPIOD_LINE_BULK_INITIALIZER), _tdo_line(NULL) | |
| { | |
| if (open_device() < 0) | |
| throw std::runtime_error("gpio periphral access failed"); | |
| } | |
| /*! | |
| * \brief destructor: release pins and peripheral | |
| */ | |
| ~MyGPIOD() { | |
| close_device(); | |
| } | |
| /*! | |
| * \brief open a device and configure pins | |
| * \return 0 on success, -1 otherwise | |
| */ | |
| int open_device(); | |
| /*! | |
| * \brief release pins and close peripheral | |
| */ | |
| void close_device(); | |
| /*! | |
| * \brief update tms, tdi pins with tck low then high, read tdo if asked and | |
| * tck low again when end | |
| * \param[in] tms: tms pin state (0/1) | |
| * \param[in] tdi: tdi pin state (0/1) | |
| * \param[in] tdo: sample tdo line when not NULL | |
| * \param[in] end: set tck low before return | |
| * \return 0 when success, -1 otherwise | |
| */ | |
| int set_pins(int tms, int tdi, uint8_t *tdo, bool end); | |
| private: | |
| /*! | |
| * Update tms tdi and tck pins state | |
| * \param[in] tck: tck pin state (0/1) | |
| * \param[in] tms: tms pin state (0/1) | |
| * \param[in] tdi: tdi pin state (0/1) | |
| * \return 0 when success, -1 otherwise | |
| */ | |
| int update_pins(int tck, int tms, int tdi); | |
| const char *_devname; /*!< /dev/gpiochipx name */ | |
| uint32_t _tms, _tck, _tdo, _tdi; /*!< JTAG pins ID */ | |
| gpiod_chip *_chip; /*!< libGPIOD peripheral */ | |
| struct gpiod_line_bulk _lines; /*!< TDI, TMS, TCK lines */ | |
| struct gpiod_line *_tdo_line; /*!< TDO only */ | |
| }; | |
| int MyGPIOD::open_device() | |
| { | |
| /* try to open device by full path (/dev/gpiochipXXX) */ | |
| _chip = gpiod_chip_open(_devname); | |
| if (!_chip) { | |
| printf("gpiod chip failed\n"); | |
| return -1; | |
| } | |
| /* TMS, TDI and TCK configuration */ | |
| uint32_t offs[3]= {_tck, _tms, _tdi}; | |
| int vals[3]= {0, 1, 0}; | |
| /* get lines */ | |
| int ret = gpiod_chip_get_lines(_chip, offs, 3, &_lines); | |
| if (ret == -1) { | |
| printf("Unable to get gpio lines\n"); | |
| return ret; | |
| } | |
| /* request lines -> all as output */ | |
| ret = gpiod_line_request_bulk_output(&_lines, "openFPGALoader", vals); | |
| if (ret < 0) { | |
| printf("Error requesting gpio lines\n"); | |
| } | |
| /* default state */ | |
| ret = gpiod_line_set_value_bulk(&_lines, vals); | |
| if (ret < 0) { | |
| printf("Error set default value\n"); | |
| } | |
| /* TDO line (input) */ | |
| _tdo_line = gpiod_chip_get_line(_chip, _tdo); | |
| if (!_tdo_line) { | |
| printf("Unable to get gpio line %d\n", _tdo); | |
| return -1; | |
| } | |
| if (gpiod_line_request_input(_tdo_line, "openFPGALoader") < 0) { | |
| printf("Error requesting gpio line %d\n", _tdo); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| void MyGPIOD::close_device() | |
| { | |
| if (_lines.lines) | |
| gpiod_line_release_bulk(&_lines); | |
| if (_tdo_line) | |
| gpiod_line_release(_tdo_line); | |
| if (_chip) | |
| gpiod_chip_close(_chip); | |
| } | |
| int MyGPIOD::update_pins(int tck, int tms, int tdi) | |
| { | |
| int vals[3] = {tck, tms, tdi}; | |
| if (gpiod_line_set_value_bulk(&_lines, vals) < 0) { | |
| printf("Unable to update gpio lines\n"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int MyGPIOD::set_pins(int tms, int tdi, uint8_t *tdo, bool end) | |
| { | |
| /* first update all lines with clk set to low (before read) */ | |
| if (update_pins(0, tms, tdi) == -1) | |
| return -1; | |
| /* keep tms/tdi and set clk set to high (sampling edge) */ | |
| if (update_pins(1, tms, tdi) == -1) | |
| return -1; | |
| /* read? after clk rise: TDO must be stable */ | |
| if (tdo) { | |
| int tmp; | |
| if ((tmp = gpiod_line_get_value(_tdo_line)) < 0) | |
| return -1; | |
| *tdo = static_cast<uint8_t>(tmp); | |
| } | |
| /* security: if it's the last bit from a buffer set clk low */ | |
| if (end) { | |
| if (update_pins(0, tms, tdi) == -1) | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| #define DEV_GPIO "gpiochip0\0" | |
| /* PIN 32 */ | |
| #define TCK 12 | |
| /* PIN 34 */ | |
| /* GND */ | |
| /* PIN 36 */ | |
| #define TDI 16 | |
| /* PIN 38 */ | |
| #define TDO 20 | |
| /* PIN 40 */ | |
| #define TMS 21 | |
| int main() | |
| { | |
| int ret = EXIT_SUCCESS; | |
| MyGPIOD mygpiod(DEV_GPIO, TMS, TCK, TDO, TDI); | |
| printf("Press Key"); | |
| getchar(); | |
| for (uint8_t c = 0; c < 16; c++) { | |
| if (mygpiod.set_pins(((c >> 1) & 0x01), (c & 0x01), NULL, c == 15) == -1) { | |
| ret = EXIT_FAILURE; | |
| break; | |
| } | |
| } | |
| char c = 0x55; | |
| for (uint8_t i = 0; i < 8; i++) { | |
| if (mygpiod.set_pins(i == 7, ((c >> i) & 0x01), NULL, i == 7) == -1) { | |
| ret = EXIT_FAILURE; | |
| break; | |
| } | |
| } | |
| return ret; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment