Working CPP way of writing data
This commit is contained in:
2
tools/.gitignore
vendored
Normal file
2
tools/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
test
|
||||
32
tools/Makefile
Normal file
32
tools/Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
TOOLCHAIN_PREFIX ?=
|
||||
|
||||
CC := $(TOOLCHAIN_PREFIX)g++
|
||||
|
||||
TARGET := test
|
||||
SRCS_C :=
|
||||
SRCS_CPP:= test.cpp digilent_jtag.cpp
|
||||
OBJS := $(SRCS_C:.c=.o) $(SRCS_CPP:.cpp=.o)
|
||||
|
||||
ADEPT_LIBDIR := /opt/packages/digilent.adept.runtime_2.27.9-x86_64/lib64
|
||||
|
||||
CFLAGS :=
|
||||
ASFLAGS :=
|
||||
LDFLAGS := -L$(ADEPT_LIBDIR) -Wl,--disable-new-dtags -Wl,-rpath,$(ADEPT_LIBDIR)
|
||||
LIBS := -ldjtg -ldmgr -ldpcomm -ldabs -ldftd2xx
|
||||
|
||||
|
||||
.PHONY: all clean size
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(OBJS)
|
||||
276
tools/digilent_jtag.cpp
Normal file
276
tools/digilent_jtag.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
#include "digilent_jtag.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include <digilent/adept/dmgr.h>
|
||||
#include <digilent/adept/djtg.h>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kDefaultIrBits = 6;
|
||||
|
||||
std::string ercToString(ERC erc) {
|
||||
char code[cchErcMax] = {0};
|
||||
char msg[cchErcMsgMax] = {0};
|
||||
if (DmgrSzFromErc(erc, code, msg)) {
|
||||
return std::string(code) + ": " + msg;
|
||||
}
|
||||
return "ERC=" + std::to_string(erc);
|
||||
}
|
||||
|
||||
inline uint8_t getBit(const uint8_t* packed_bits, int bit_idx) {
|
||||
return static_cast<uint8_t>((packed_bits[bit_idx / 8] >> (bit_idx % 8)) & 0x1u);
|
||||
}
|
||||
|
||||
inline void setBit(uint8_t* packed_bits, int bit_idx, uint8_t bit) {
|
||||
const uint8_t mask = static_cast<uint8_t>(1u << (bit_idx % 8));
|
||||
if (bit & 0x1u) {
|
||||
packed_bits[bit_idx / 8] |= mask;
|
||||
} else {
|
||||
packed_bits[bit_idx / 8] &= static_cast<uint8_t>(~mask);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DigilentJtag::DigilentJtag() : hif_(hifInvalid), enabled_port_(-1), last_error_() {}
|
||||
|
||||
DigilentJtag::~DigilentJtag() { close(); }
|
||||
|
||||
bool DigilentJtag::open(int port) {
|
||||
close();
|
||||
|
||||
int count = 0;
|
||||
if (!DmgrEnumDevices(&count)) {
|
||||
return setErrorFromDmgr("DmgrEnumDevices");
|
||||
}
|
||||
if (count <= 0) {
|
||||
return setError("open: no Digilent devices found");
|
||||
}
|
||||
|
||||
DVC dvc{};
|
||||
if (!DmgrGetDvc(0, &dvc)) {
|
||||
return setErrorFromDmgr("DmgrGetDvc");
|
||||
}
|
||||
|
||||
return open(std::string(dvc.szConn), port);
|
||||
}
|
||||
|
||||
bool DigilentJtag::open(const std::string& selector, int port) {
|
||||
close();
|
||||
|
||||
if (selector.empty()) {
|
||||
return setError("open: selector is empty");
|
||||
}
|
||||
|
||||
std::vector<char> sel(selector.begin(), selector.end());
|
||||
sel.push_back('\0');
|
||||
|
||||
if (!DmgrOpen(&hif_, sel.data())) {
|
||||
hif_ = hifInvalid;
|
||||
return setErrorFromDmgr("DmgrOpen");
|
||||
}
|
||||
|
||||
if (!DjtgEnableEx(hif_, static_cast<INT32>(port))) {
|
||||
if (!DjtgEnable(hif_)) {
|
||||
DmgrClose(hif_);
|
||||
hif_ = hifInvalid;
|
||||
return setErrorFromDmgr("DjtgEnableEx/DjtgEnable");
|
||||
}
|
||||
enabled_port_ = 0;
|
||||
} else {
|
||||
enabled_port_ = port;
|
||||
}
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DigilentJtag::close() {
|
||||
if (hif_ != hifInvalid) {
|
||||
(void)DjtgDisable(hif_);
|
||||
(void)DmgrClose(hif_);
|
||||
}
|
||||
hif_ = hifInvalid;
|
||||
enabled_port_ = -1;
|
||||
}
|
||||
|
||||
bool DigilentJtag::isOpen() const { return hif_ != hifInvalid; }
|
||||
|
||||
HIF DigilentJtag::handle() const { return hif_; }
|
||||
|
||||
bool DigilentJtag::setSpeed(uint32_t requested_hz, uint32_t* actual_hz) {
|
||||
if (!isOpen()) {
|
||||
return setError("setSpeed: device not open");
|
||||
}
|
||||
|
||||
DWORD actual = 0;
|
||||
if (!DjtgSetSpeed(hif_, static_cast<DWORD>(requested_hz), &actual)) {
|
||||
return setErrorFromDmgr("DjtgSetSpeed");
|
||||
}
|
||||
if (actual_hz) {
|
||||
*actual_hz = static_cast<uint32_t>(actual);
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::setChain(int chain, int ir_bits) {
|
||||
uint32_t opcode = 0;
|
||||
if (chain == 1) {
|
||||
opcode = 0x02; // USER1 on Spartan-6
|
||||
} else if (chain == 2) {
|
||||
opcode = 0x03; // USER2 on Spartan-6
|
||||
} else {
|
||||
return setError("setChain: unsupported chain index (expected 1 or 2)");
|
||||
}
|
||||
return setInstruction(opcode, ir_bits);
|
||||
}
|
||||
|
||||
bool DigilentJtag::setInstruction(uint32_t instruction, int ir_bits) {
|
||||
if (!isOpen()) {
|
||||
return setError("setInstruction: device not open");
|
||||
}
|
||||
if (ir_bits <= 0 || ir_bits > 64) {
|
||||
return setError("setInstruction: ir_bits out of range");
|
||||
}
|
||||
|
||||
// Force Test-Logic-Reset, then RTI.
|
||||
uint8_t tlr = 0x3f; // 6 ones
|
||||
if (!putTmsBits(&tlr, 6)) return false;
|
||||
uint8_t rti = 0x00; // one zero
|
||||
if (!putTmsBits(&rti, 1)) return false;
|
||||
|
||||
// RTI -> Shift-IR : 1,1,0,0 (LSB-first 0b0011)
|
||||
uint8_t to_shift_ir = 0x03;
|
||||
if (!putTmsBits(&to_shift_ir, 4)) return false;
|
||||
|
||||
std::vector<uint8_t> tx(static_cast<size_t>((ir_bits + 7) / 8), 0);
|
||||
for (int i = 0; i < ir_bits && i < 64; ++i) {
|
||||
if ((instruction >> i) & 0x1u) {
|
||||
tx[static_cast<size_t>(i / 8)] |= static_cast<uint8_t>(1u << (i % 8));
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> rx(tx.size(), 0);
|
||||
|
||||
if (ir_bits > 1) {
|
||||
if (!putTdiBits(false, tx.data(), rx.data(), ir_bits - 1)) return false;
|
||||
}
|
||||
const uint8_t last_tx = getBit(tx.data(), ir_bits - 1);
|
||||
uint8_t last_rx = 0;
|
||||
if (!putTdiBits(true, &last_tx, &last_rx, 1)) return false;
|
||||
|
||||
// Exit1-IR -> Update-IR -> Idle: 1,0
|
||||
uint8_t to_idle = 0x01;
|
||||
if (!putTmsBits(&to_idle, 2)) return false;
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::shiftData(const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count) {
|
||||
if (!isOpen()) {
|
||||
return setError("shiftData: device not open");
|
||||
}
|
||||
if (bit_count <= 0) {
|
||||
return setError("shiftData: bit_count must be > 0");
|
||||
}
|
||||
|
||||
const size_t nbytes = static_cast<size_t>((bit_count + 7) / 8);
|
||||
std::vector<uint8_t> tx_zeros;
|
||||
if (!tx_bits) {
|
||||
tx_zeros.assign(nbytes, 0);
|
||||
tx_bits = tx_zeros.data();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rx_tmp;
|
||||
if (!rx_bits) {
|
||||
rx_tmp.assign(nbytes, 0);
|
||||
rx_bits = rx_tmp.data();
|
||||
} else {
|
||||
std::memset(rx_bits, 0, nbytes);
|
||||
}
|
||||
|
||||
if (!enterShiftDR()) return false;
|
||||
|
||||
if (bit_count > 1) {
|
||||
if (!putTdiBits(false, tx_bits, rx_bits, bit_count - 1)) return false;
|
||||
}
|
||||
|
||||
const uint8_t tx_last = getBit(tx_bits, bit_count - 1);
|
||||
uint8_t rx_last = 0;
|
||||
if (!putTdiBits(true, &tx_last, &rx_last, 1)) return false;
|
||||
setBit(rx_bits, bit_count - 1, rx_last & 0x1u);
|
||||
|
||||
if (!leaveShiftToIdle()) return false;
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::shiftData(const std::vector<uint8_t>& tx_bits, std::vector<uint8_t>* rx_bits, int bit_count) {
|
||||
if (bit_count <= 0) {
|
||||
return setError("shiftData(vector): bit_count must be > 0");
|
||||
}
|
||||
const size_t nbytes = static_cast<size_t>((bit_count + 7) / 8);
|
||||
if (tx_bits.size() < nbytes) {
|
||||
return setError("shiftData(vector): tx_bits is smaller than required bit_count");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> local_rx;
|
||||
local_rx.assign(nbytes, 0);
|
||||
|
||||
if (!shiftData(tx_bits.data(), local_rx.data(), bit_count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rx_bits) {
|
||||
*rx_bits = std::move(local_rx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& DigilentJtag::lastError() const { return last_error_; }
|
||||
|
||||
bool DigilentJtag::enterShiftDR() {
|
||||
// Idle -> Select-DR -> Capture-DR -> Shift-DR : 1,0,0
|
||||
uint8_t to_shift_dr = 0x01;
|
||||
return putTmsBits(&to_shift_dr, 3);
|
||||
}
|
||||
|
||||
bool DigilentJtag::leaveShiftToIdle() {
|
||||
// Exit1-DR -> Update-DR -> Idle : 1,0
|
||||
uint8_t to_idle = 0x01;
|
||||
return putTmsBits(&to_idle, 2);
|
||||
}
|
||||
|
||||
bool DigilentJtag::putTmsBits(const uint8_t* tms_bits, int bit_count) {
|
||||
if (!DjtgPutTmsBits(hif_, fFalse, const_cast<uint8_t*>(tms_bits), nullptr, static_cast<DWORD>(bit_count), fFalse)) {
|
||||
return setErrorFromDmgr("DjtgPutTmsBits");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::putTdiBits(bool tms, const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count) {
|
||||
if (!DjtgPutTdiBits(
|
||||
hif_,
|
||||
tms ? fTrue : fFalse,
|
||||
const_cast<uint8_t*>(tx_bits),
|
||||
rx_bits,
|
||||
static_cast<DWORD>(bit_count),
|
||||
fFalse)) {
|
||||
return setErrorFromDmgr("DjtgPutTdiBits");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::setError(const std::string& msg) {
|
||||
last_error_ = msg;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DigilentJtag::setErrorFromDmgr(const std::string& where) {
|
||||
last_error_ = where + " failed: " + ercToString(DmgrGetLastError());
|
||||
return false;
|
||||
}
|
||||
52
tools/digilent_jtag.hpp
Normal file
52
tools/digilent_jtag.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <digilent/adept/dpcdecl.h>
|
||||
|
||||
class DigilentJtag {
|
||||
public:
|
||||
DigilentJtag();
|
||||
~DigilentJtag();
|
||||
|
||||
DigilentJtag(const DigilentJtag&) = delete;
|
||||
DigilentJtag& operator=(const DigilentJtag&) = delete;
|
||||
|
||||
bool open(int port = 0);
|
||||
bool open(const std::string& selector, int port = 0);
|
||||
void close();
|
||||
|
||||
bool isOpen() const;
|
||||
HIF handle() const;
|
||||
|
||||
bool setSpeed(uint32_t requested_hz, uint32_t* actual_hz = nullptr);
|
||||
|
||||
// For Spartan-6 style USER chains:
|
||||
// chain=1 -> USER1 opcode 0x02, chain=2 -> USER2 opcode 0x03
|
||||
bool setChain(int chain, int ir_bits = 6);
|
||||
|
||||
bool setInstruction(uint32_t instruction, int ir_bits);
|
||||
|
||||
// Shifts one DR transaction from Idle -> ShiftDR -> UpdateDR -> Idle.
|
||||
// tx_bits is LSB-first in packed byte form.
|
||||
// rx_bits, when non-null, receives captured TDO bits in the same packing.
|
||||
bool shiftData(const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count);
|
||||
|
||||
bool shiftData(const std::vector<uint8_t>& tx_bits, std::vector<uint8_t>* rx_bits, int bit_count);
|
||||
|
||||
const std::string& lastError() const;
|
||||
|
||||
private:
|
||||
bool enterShiftDR();
|
||||
bool leaveShiftToIdle();
|
||||
bool putTmsBits(const uint8_t* tms_bits, int bit_count);
|
||||
bool putTdiBits(bool tms, const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count);
|
||||
bool setError(const std::string& msg);
|
||||
bool setErrorFromDmgr(const std::string& where);
|
||||
|
||||
HIF hif_;
|
||||
int enabled_port_;
|
||||
std::string last_error_;
|
||||
};
|
||||
19
tools/test.cpp
Normal file
19
tools/test.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "digilent_jtag.hpp"
|
||||
|
||||
int main(int argc, char** argv){
|
||||
|
||||
DigilentJtag jtag;
|
||||
if(!jtag.open()){
|
||||
printf("Could not open programmer\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
jtag.setChain(1);
|
||||
|
||||
uint8_t data_out = 0xAB;
|
||||
uint8_t data_in;
|
||||
jtag.shiftData(&data_out, &data_in, 8);
|
||||
|
||||
jtag.close();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user