Working CPP way of writing data

This commit is contained in:
2026-02-24 16:40:17 +01:00
parent 8f4e887b9d
commit 9930ce4461
10 changed files with 803 additions and 171 deletions

View File

@@ -21,7 +21,7 @@ device = xc6slx9
package = tqg144
speedgrade = -2
toplevel = top_generic
xst_opts = -vlgincdir rtl/util
xst_opts = -vlgincdir rtl/util -keep_hierarchy yes
files_verilog = rtl/util/conv.vh
rtl/toplevel/top_generic.v
rtl/core/nco_q15.v
@@ -59,7 +59,8 @@ files_verilog = rtl/util/conv.vh
rtl/serv/serving.v
rtl/wb/wb_gpio.v
rtl/wb/wb_gpio_banks.v
rtl/core/soclet.v
rtl/arch/spartan-6/jtag_if.v
rtl/core/mcu.v
files_con = boards/mimas_v1/constraints.ucf
files_other = rtl/util/rc_alpha_q15.vh
rtl/util/clog2.vh

View File

@@ -29,6 +29,7 @@ module jtag_if #(
.SHIFT(o_shift),
.TCK(o_tck),
.TDI(o_tdi),
.TDO(i_tdo),
.UPDATE(o_update)
);
endmodule

414
rtl/core/mcu.v Normal file
View File

@@ -0,0 +1,414 @@
`timescale 1ns/1ps
`include "../util/clog2.vh"
module mcu #(
parameter memfile = "",
parameter memsize = 8192,
parameter sim = 1'b0
)(
input wire i_clk,
input wire i_rst,
input wire [31:0] i_GPI_A,
input wire [31:0] i_GPI_B,
input wire [31:0] i_GPI_C,
input wire [31:0] i_GPI_D,
output wire [31:0] o_GPO_A,
output wire [31:0] o_GPO_B,
output wire [31:0] o_GPO_C,
output wire [31:0] o_GPO_D
);
localparam WITH_CSR = 1;
localparam regs = 32+WITH_CSR*4;
localparam rf_width = 8;
wire rst;
wire rst_mem_reason;
wire timer_irq;
assign rst = i_rst | rst_mem_reason;
assign timer_irq = 1'b0;
// Busses
// CPU->memory
wire [31:0] wb_mem_adr;
wire [31:0] wb_mem_dat;
wire [3:0] wb_mem_sel;
wire wb_mem_we;
wire wb_mem_stb;
wire [31:0] wb_mem_rdt;
wire wb_mem_ack;
// CPU->peripherals
wire [31:0] wb_ext_adr;
wire [31:0] wb_ext_dat;
wire [3:0] wb_ext_sel;
wire wb_ext_we;
wire wb_ext_stb;
wire [31:0] wb_ext_rdt;
wire wb_ext_ack;
// CPU->RF
wire [6+WITH_CSR:0] rf_waddr;
wire [rf_width-1:0] rf_wdata;
wire rf_wen;
wire [6+WITH_CSR:0] rf_raddr;
wire [rf_width-1:0] rf_rdata;
wire rf_ren;
// combined RF and mem bus to actual RAM
wire [`CLOG2(memsize)-1:0] sram_waddr;
wire [rf_width-1:0] sram_wdata;
wire sram_wen;
wire [`CLOG2(memsize)-1:0] sram_raddr;
wire [rf_width-1:0] sram_rdata;
wire sram_ren;
// GPIO
wire [4*32-1:0] GPO;
wire [4*32-1:0] GPI;
assign o_GPO_A = GPO[32*1-1:32*0];
assign o_GPO_B = GPO[32*2-1:32*1];
assign o_GPO_C = GPO[32*3-1:32*2];
assign o_GPO_D = GPO[32*4-1:32*3];
assign GPI[32*1-1:32*0] = i_GPI_A;
assign GPI[32*2-1:32*1] = i_GPI_B;
assign GPI[32*3-1:32*2] = i_GPI_C;
assign GPI[32*4-1:32*3] = i_GPI_D;
// SERV core with mux splitting dbus into mem and ext and
// arbiter combining mem and ibus
// separate rst line to let other hardware keep core under reset
servile #(
.reset_pc(32'h0000_0000),
.reset_strategy("MINI"),
.rf_width(rf_width),
.sim(sim),
.with_csr(WITH_CSR),
.with_c(0),
.with_mdu(0)
) servile (
.i_clk(i_clk),
.i_rst(rst),
.i_timer_irq(timer_irq),
//Memory interface
.o_wb_mem_adr(wb_mem_adr),
.o_wb_mem_dat(wb_mem_dat),
.o_wb_mem_sel(wb_mem_sel),
.o_wb_mem_we(wb_mem_we),
.o_wb_mem_stb(wb_mem_stb),
.i_wb_mem_rdt(wb_mem_rdt),
.i_wb_mem_ack(wb_mem_ack),
//Extension interface
.o_wb_ext_adr(wb_ext_adr),
.o_wb_ext_dat(wb_ext_dat),
.o_wb_ext_sel(wb_ext_sel),
.o_wb_ext_we(wb_ext_we),
.o_wb_ext_stb(wb_ext_stb),
.i_wb_ext_rdt(wb_ext_rdt),
.i_wb_ext_ack(wb_ext_ack),
//RF IF
.o_rf_waddr(rf_waddr),
.o_rf_wdata(rf_wdata),
.o_rf_wen(rf_wen),
.o_rf_raddr(rf_raddr),
.o_rf_ren(rf_ren),
.i_rf_rdata(rf_rdata)
);
// WB arbiter combining RF and mem interfaces into 1
// Last 128 bytes are used for registers
servile_rf_mem_if #(
.depth(memsize),
.rf_regs(regs)
) rf_mem_if (
.i_clk (i_clk),
.i_rst (i_rst),
.i_waddr(rf_waddr),
.i_wdata(rf_wdata),
.i_wen(rf_wen),
.i_raddr(rf_raddr),
.o_rdata(rf_rdata),
.i_ren(rf_ren),
.o_sram_waddr(sram_waddr),
.o_sram_wdata(sram_wdata),
.o_sram_wen(sram_wen),
.o_sram_raddr(sram_raddr),
.i_sram_rdata(sram_rdata),
// .o_sram_ren(sram_ren),
.i_wb_adr(wb_mem_adr[`CLOG2(memsize)-1:2]),
.i_wb_stb(wb_mem_stb),
.i_wb_we(wb_mem_we) ,
.i_wb_sel(wb_mem_sel),
.i_wb_dat(wb_mem_dat),
.o_wb_rdt(wb_mem_rdt),
.o_wb_ack(wb_mem_ack)
);
memory #(
.memfile(memfile),
.depth(memsize),
.sim(sim)
) mem (
.i_clk(i_clk),
.i_waddr(sram_waddr),
.i_wdata(sram_wdata),
.i_wen(sram_wen),
.i_raddr(sram_raddr),
.o_rdata(sram_rdata),
.o_core_reset(rst_mem_reason)
);
wb_gpio_banks #(
.BASE_ADDR(32'h40000000),
.NUM_BANKS(4)
) gpio (
.i_wb_clk(i_clk),
.i_wb_rst(rst),
.i_wb_dat(wb_ext_dat),
.i_wb_adr(wb_ext_adr),
.i_wb_we(wb_ext_we),
.i_wb_stb(wb_ext_stb),
.i_wb_sel(wb_ext_sel),
.o_wb_rdt(wb_ext_rdt),
.o_wb_ack(wb_ext_ack),
.i_gpio(GPI),
.o_gpio(GPO)
);
endmodule
module jtag_mem_protocol #(
parameter aw = 8,
parameter chain = 1
)(
input wire i_clk,
input wire i_rst,
input wire [7:0] i_mem_rdata,
output reg o_mem_wen,
output reg [7:0] o_mem_wdata,
output reg [aw-1:0] o_mem_waddr,
output reg [aw-1:0] o_mem_raddr,
output reg o_core_reset
);
localparam FRAME_BITS = 42;
// Frame format (LSB-first on JTAG shift stream):
// [0] : core reset
// [1] : write (1=write, 0=read)
// [33:2] : address
// [41:34] : data
wire [31:0] cmd_addr_full;
wire cmd_reset;
wire cmd_write;
wire [7:0] cmd_data;
// JTAG interface wires
wire jtag_tck;
wire jtag_tdi;
wire jtag_drck;
wire jtag_capture;
wire jtag_shift;
wire jtag_update;
wire jtag_runtest;
wire jtag_reset;
wire jtag_sel;
// JTAG clock-domain regs
reg [FRAME_BITS-1:0] shift_q;
reg [FRAME_BITS-1:0] cmd_jtag_q;
reg update_toggle_jtag_q;
reg [FRAME_BITS-1:0] resp_meta_q;
reg [FRAME_BITS-1:0] resp_sync_q;
// Core clock-domain CDC regs
reg update_toggle_meta_q;
reg update_toggle_sync_q;
reg update_toggle_sync_d_q;
reg [FRAME_BITS-1:0] cmd_meta_q;
reg [FRAME_BITS-1:0] cmd_sync_q;
// Core clock-domain protocol state
reg [FRAME_BITS-1:0] resp_core_q;
reg read_pending_q;
reg [31:0] read_addr_full_q;
assign cmd_reset = cmd_sync_q[0];
assign cmd_write = cmd_sync_q[1];
assign cmd_addr_full = cmd_sync_q[33:2];
assign cmd_data = cmd_sync_q[41:34];
jtag_if #(
.chain(chain)
) jtag (
.i_tdo(shift_q[0]),
.o_tck(jtag_tck),
.o_tdi(jtag_tdi),
.o_drck(jtag_drck),
.o_capture(jtag_capture),
.o_shift(jtag_shift),
.o_update(jtag_update),
.o_runtest(jtag_runtest),
.o_reset(jtag_reset),
.o_sel(jtag_sel)
);
// JTAG domain: shift register and update event capture.
always @(posedge jtag_drck or posedge jtag_reset or posedge i_rst) begin
if (jtag_reset || i_rst) begin
shift_q <= {FRAME_BITS{1'b0}};
resp_meta_q <= {FRAME_BITS{1'b0}};
resp_sync_q <= {FRAME_BITS{1'b0}};
end else begin
resp_meta_q <= resp_core_q;
resp_sync_q <= resp_meta_q;
if (jtag_sel && jtag_capture) begin
shift_q <= resp_sync_q;
end else if (jtag_sel && jtag_shift) begin
shift_q <= {jtag_tdi, shift_q[FRAME_BITS-1:1]};
end
end
end
always @(posedge jtag_update or posedge jtag_reset or posedge i_rst) begin
if (jtag_reset || i_rst) begin
cmd_jtag_q <= {FRAME_BITS{1'b0}};
update_toggle_jtag_q <= 1'b0;
end else if (jtag_sel) begin
cmd_jtag_q <= shift_q;
update_toggle_jtag_q <= ~update_toggle_jtag_q;
end
end
// Core domain: receive command and execute against RAM port-B.
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
update_toggle_meta_q <= 1'b0;
update_toggle_sync_q <= 1'b0;
update_toggle_sync_d_q <= 1'b0;
cmd_meta_q <= {FRAME_BITS{1'b0}};
cmd_sync_q <= {FRAME_BITS{1'b0}};
o_mem_wen <= 1'b0;
o_mem_wdata <= 8'h00;
o_mem_waddr <= {aw{1'b0}};
o_mem_raddr <= {aw{1'b0}};
o_core_reset <= 1'b0;
resp_core_q <= {FRAME_BITS{1'b0}};
read_pending_q <= 1'b0;
read_addr_full_q <= 32'h0000_0000;
end else begin
// Defaults are one-cycle strobes for write/reset.
o_mem_wen <= 1'b0;
o_core_reset <= 1'b0;
update_toggle_meta_q <= update_toggle_jtag_q;
update_toggle_sync_q <= update_toggle_meta_q;
update_toggle_sync_d_q <= update_toggle_sync_q;
cmd_meta_q <= cmd_jtag_q;
cmd_sync_q <= cmd_meta_q;
// Complete pending read (port-B read is synchronous).
if (read_pending_q) begin
read_pending_q <= 1'b0;
resp_core_q[0] <= 1'b0;
resp_core_q[1] <= 1'b0;
resp_core_q[33:2] <= read_addr_full_q;
resp_core_q[41:34] <= i_mem_rdata;
end
// New JTAG command arrived.
if (update_toggle_sync_q ^ update_toggle_sync_d_q) begin
if (cmd_reset) begin
o_core_reset <= 1'b1;
end
if (cmd_write) begin
o_mem_waddr <= cmd_addr_full[aw-1:0];
o_mem_wdata <= cmd_data;
o_mem_wen <= 1'b1;
resp_core_q[0] <= cmd_reset;
resp_core_q[1] <= 1'b1;
resp_core_q[33:2] <= cmd_addr_full;
resp_core_q[41:34] <= cmd_data;
end else begin
o_mem_raddr <= cmd_addr_full[aw-1:0];
read_pending_q <= 1'b1;
read_addr_full_q <= cmd_addr_full;
end
end
end
end
// Keep lint quiet for currently-unused JTAG outputs.
wire _unused_tck;
wire _unused_runtest;
assign _unused_tck = jtag_tck;
assign _unused_runtest = jtag_runtest;
endmodule
module memory #(
parameter memfile = "",
parameter depth = 256,
parameter sim = 1'b0,
localparam aw = `CLOG2(depth)
)(
input wire i_clk,
input wire [aw-1:0] i_waddr,
input wire [7:0] i_wdata,
input wire i_wen,
input wire [aw-1:0] i_raddr,
output reg [7:0] o_rdata,
output wire o_core_reset
);
// The actual memory
reg [7:0]mem [0:depth-1];
// Second interface
wire wen_b;
wire [7:0] wdata_b;
wire [aw-1:0] waddr_b;
reg [7:0] rdata_b;
wire [aw-1:0] raddr_b;
jtag_mem_protocol #(
.aw(aw),
.chain(1)
) jtag_mem (
.i_clk(i_clk),
.i_rst(1'b0),
.i_mem_rdata(rdata_b),
.o_mem_wen(wen_b),
.o_mem_wdata(wdata_b),
.o_mem_waddr(waddr_b),
.o_mem_raddr(raddr_b),
.o_core_reset(o_core_reset)
);
// Read/Write
always @(posedge i_clk) begin
// main interface
if (i_wen)
mem[i_waddr] <= i_wdata;
o_rdata <= mem[i_raddr];
// second interface
if (wen_b)
mem[waddr_b] <= wdata_b;
rdata_b <= mem[raddr_b];
end
// Preload memory
integer i;
initial begin
if(sim==1'b1) begin
for (i = 0; i < depth; i = i + 1)
mem[i] = 8'h00;
end
if(|memfile) begin
$display("Preloading %m from %s", memfile);
$readmemh(memfile, mem);
end
end
endmodule

View File

@@ -7,11 +7,13 @@ module top_generic(
output wire led_green,
output wire led_red,
output wire[5:0] r2r
output wire[5:0] r2r,
output wire[7:0] LED
);
`include "conv.vh"
assign led_green = 1'b0;
assign led_red = 1'b0;
assign LED = 8'h00;
// Clocking
wire clk_100;
@@ -27,7 +29,7 @@ module top_generic(
wire [31:0] GPIO_C;
wire [31:0] GPIO_D;
soclet #(
mcu #(
.memfile("../sw/sweep/sweep.hex")
) mcu (
.i_clk(clk_15),

View File

@@ -1,167 +0,0 @@
#!/usr/bin/env python3
"""Write USER JTAG data via iMPACT using SVF + play flow."""
from __future__ import annotations
import argparse
import os
import shutil
import subprocess
import sys
import tempfile
DEFAULT_IMPACT = "/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/impact"
DEFAULT_SETTINGS = "/opt/Xilinx/14.7/ISE_DS/settings64.sh"
def _parse_int(value: str) -> int:
return int(value, 0)
def _resolve_impact(binary: str | None) -> str | None:
if binary:
return binary if os.path.isfile(binary) else shutil.which(binary)
if os.path.isfile(DEFAULT_IMPACT):
return DEFAULT_IMPACT
return shutil.which("impact")
def _fmt_hex_for_bits(value: int, bits: int) -> str:
masked = value & ((1 << bits) - 1)
nibbles = (bits + 3) // 4
return f"{masked:0{nibbles}X}"
def main() -> int:
parser = argparse.ArgumentParser(
description="Write USER JTAG data to Spartan-6 via iMPACT play (SVF)."
)
parser.add_argument(
"--input",
required=True,
help="Input file to stream over USER JTAG (written byte-by-byte).",
)
parser.add_argument("--output", type=str, default=None)
parser.add_argument("--dr-bits", type=int, default=8, help="DR width in bits (default: 8).")
parser.add_argument("--ir-value", type=_parse_int, default=0x02, help="IR opcode (default USER1: 0x02).")
parser.add_argument("--ir-bits", type=int, default=6, help="IR width in bits (default: 6).")
parser.add_argument("--serial", default=None, help="Cable ESN/serial (example: D306180FABCD).")
parser.add_argument("--impact", default=None, help=f"Path/name of impact binary (default: {DEFAULT_IMPACT}).")
parser.add_argument(
"--settings",
default=DEFAULT_SETTINGS,
help=f"Xilinx settings script sourced for this run only (default: {DEFAULT_SETTINGS}).",
)
parser.add_argument("--dry-run", action="store_true", help="Print generated SVF/CMD and exit.")
args = parser.parse_args()
if args.dr_bits <= 0 or args.ir_bits <= 0:
print("error: --dr-bits and --ir-bits must be > 0", file=sys.stderr)
return 2
if args.dr_bits != 8:
print("error: this file-stream mode currently supports only --dr-bits 8", file=sys.stderr)
return 2
impact = _resolve_impact(args.impact)
if impact is None:
print("error: impact binary not found", file=sys.stderr)
return 127
if not os.path.isfile(args.input):
print(f'error: input file not found: "{args.input}"', file=sys.stderr)
return 2
ir_hex = _fmt_hex_for_bits(args.ir_value, args.ir_bits)
with open(args.input, "rb") as f:
payload = f.read()
if len(payload) == 0:
print("error: input file is empty", file=sys.stderr)
return 2
svf_lines = [
"! Auto-generated by jtag_write_user_impact.py",
f"! Source file: {args.input} ({len(payload)} bytes)",
"TRST ABSENT;",
"ENDIR IDLE;",
"ENDDR IDLE;",
"STATE RESET;",
"STATE IDLE;",
f"SIR {args.ir_bits} TDI ({ir_hex});",
]
for byte in payload:
svf_lines.append(f"SDR 8 TDI ({byte:02X});")
svf_lines += [
"RUNTEST 16 TCK;",
"STATE IDLE;",
"",
]
svf_text = "\n".join(svf_lines)
cable_cmd = "setCable -port auto"
if args.serial:
cable_cmd += f" -esn {args.serial}"
if args.dry_run:
print("### SVF ###")
preview_limit = 64
if len(payload) <= preview_limit:
print(svf_text, end="")
else:
preview = "\n".join(svf_lines[:8 + preview_limit])
print(preview)
print(f"... ({len(payload) - preview_limit} more SDR lines omitted)")
print("### iMPACT CMD ###")
print("setMode -bs")
print(cable_cmd)
print("addDevice -p 1 -file <generated.svf>")
print("play")
print("quit")
return 0
svf_path = None
cmd_path = None
try:
with tempfile.NamedTemporaryFile("w", suffix=".svf", delete=False) as sf:
sf.write(svf_text)
svf_path = sf.name
if args.output is not None:
with open(args.output, "w") as sf:
sf.write(svf_text)
cmd_text = "\n".join(
[
"setMode -bs",
cable_cmd,
f'addDevice -p 1 -file "{svf_path}"',
"play",
"quit",
"",
]
)
with tempfile.NamedTemporaryFile("w", suffix=".cmd", delete=False) as cf:
cf.write(cmd_text)
cmd_path = cf.name
shell_cmd = (
f'source "{args.settings}" >/dev/null 2>&1 && '
f'"{impact}" -batch "{cmd_path}"'
)
proc = subprocess.run(["bash", "-lc", shell_cmd], text=True, capture_output=True)
output = (proc.stdout or "") + (proc.stderr or "")
print(output, end="")
return proc.returncode
finally:
if cmd_path is not None:
try:
os.unlink(cmd_path)
except OSError:
pass
if svf_path is not None:
try:
os.unlink(svf_path)
except OSError:
pass
if __name__ == "__main__":
raise SystemExit(main())

2
tools/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.o
test

32
tools/Makefile Normal file
View 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
View 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
View 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
View 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;
}