jtag memory interface working
This commit is contained in:
21
project.cfg
21
project.cfg
@@ -21,7 +21,7 @@ device = xc6slx9
|
|||||||
package = tqg144
|
package = tqg144
|
||||||
speedgrade = -2
|
speedgrade = -2
|
||||||
toplevel = top_generic
|
toplevel = top_generic
|
||||||
xst_opts = -vlgincdir rtl/util -keep_hierarchy yes
|
xst_opts = -vlgincdir rtl/util
|
||||||
files_verilog = rtl/util/conv.vh
|
files_verilog = rtl/util/conv.vh
|
||||||
rtl/toplevel/top_generic.v
|
rtl/toplevel/top_generic.v
|
||||||
rtl/core/nco_q15.v
|
rtl/core/nco_q15.v
|
||||||
@@ -57,9 +57,10 @@ files_verilog = rtl/util/conv.vh
|
|||||||
rtl/serv/servile.v
|
rtl/serv/servile.v
|
||||||
rtl/serv/serving_ram.v
|
rtl/serv/serving_ram.v
|
||||||
rtl/serv/serving.v
|
rtl/serv/serving.v
|
||||||
|
rtl/arch/spartan-6/jtag_if.v
|
||||||
rtl/wb/wb_gpio.v
|
rtl/wb/wb_gpio.v
|
||||||
rtl/wb/wb_gpio_banks.v
|
rtl/wb/wb_gpio_banks.v
|
||||||
rtl/arch/spartan-6/jtag_if.v
|
rtl/wb/jtag_wb_bridge.v
|
||||||
rtl/core/mcu.v
|
rtl/core/mcu.v
|
||||||
files_con = boards/mimas_v1/constraints.ucf
|
files_con = boards/mimas_v1/constraints.ucf
|
||||||
files_other = rtl/util/rc_alpha_q15.vh
|
files_other = rtl/util/rc_alpha_q15.vh
|
||||||
@@ -74,11 +75,13 @@ device = xc6slx9
|
|||||||
package = tqg144
|
package = tqg144
|
||||||
speedgrade = -2
|
speedgrade = -2
|
||||||
toplevel = top_jtag
|
toplevel = top_jtag
|
||||||
xst_opts = -vlgincdir rtl/util
|
xst_opts = -vlgincdir rtl/util -keep_hierarchy yes
|
||||||
files_other =
|
files_other =
|
||||||
files_con = boards/mimas_v1/constraints.ucf
|
files_con = boards/mimas_v1/constraints.ucf
|
||||||
files_verilog = rtl/arch/spartan-6/jtag_if.v
|
files_verilog = rtl/arch/spartan-6/jtag_if.v
|
||||||
rtl/arch/spartan-6/clk_gen.v
|
rtl/arch/spartan-6/clk_gen.v
|
||||||
|
rtl/wb/jtag_wb_bridge.v
|
||||||
|
rtl/wb/wb_gpio.v
|
||||||
rtl/toplevel/top_jtag.v
|
rtl/toplevel/top_jtag.v
|
||||||
|
|
||||||
[target.svftest]
|
[target.svftest]
|
||||||
@@ -87,4 +90,16 @@ runtime = all
|
|||||||
toplevel = tb_svf
|
toplevel = tb_svf
|
||||||
files_verilog = sim/tb/tb_svf.v
|
files_verilog = sim/tb/tb_svf.v
|
||||||
sim/overrides/jtag_if.v
|
sim/overrides/jtag_if.v
|
||||||
|
rtl/core/cdc_strobed.v
|
||||||
files_other = sim/other/test.svf
|
files_other = sim/other/test.svf
|
||||||
|
|
||||||
|
[target.tools]
|
||||||
|
toolchain = make
|
||||||
|
output_files = tools/test
|
||||||
|
buildroot = tools
|
||||||
|
files_makefile = tools/Makefile
|
||||||
|
files_other = tools/digilent_jtag.cpp
|
||||||
|
tools/digilent_jtag.hpp
|
||||||
|
tools/argparse.cpp
|
||||||
|
tools/argparse.hpp
|
||||||
|
tools/test.cpp
|
||||||
|
|||||||
246
rtl/core/mcu.v
246
rtl/core/mcu.v
@@ -153,6 +153,7 @@ module mcu #(
|
|||||||
.sim(sim)
|
.sim(sim)
|
||||||
) mem (
|
) mem (
|
||||||
.i_clk(i_clk),
|
.i_clk(i_clk),
|
||||||
|
.i_rst(i_rst),
|
||||||
.i_waddr(sram_waddr),
|
.i_waddr(sram_waddr),
|
||||||
.i_wdata(sram_wdata),
|
.i_wdata(sram_wdata),
|
||||||
.i_wen(sram_wen),
|
.i_wen(sram_wen),
|
||||||
@@ -180,175 +181,6 @@ module mcu #(
|
|||||||
|
|
||||||
endmodule
|
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 #(
|
module memory #(
|
||||||
parameter memfile = "",
|
parameter memfile = "",
|
||||||
parameter depth = 256,
|
parameter depth = 256,
|
||||||
@@ -356,6 +188,7 @@ module memory #(
|
|||||||
localparam aw = `CLOG2(depth)
|
localparam aw = `CLOG2(depth)
|
||||||
)(
|
)(
|
||||||
input wire i_clk,
|
input wire i_clk,
|
||||||
|
input wire i_rst,
|
||||||
input wire [aw-1:0] i_waddr,
|
input wire [aw-1:0] i_waddr,
|
||||||
input wire [7:0] i_wdata,
|
input wire [7:0] i_wdata,
|
||||||
input wire i_wen,
|
input wire i_wen,
|
||||||
@@ -364,38 +197,61 @@ module memory #(
|
|||||||
output wire o_core_reset
|
output wire o_core_reset
|
||||||
);
|
);
|
||||||
// The actual memory
|
// The actual memory
|
||||||
reg [7:0]mem [0:depth-1];
|
reg [7:0] mem [0:depth-1];
|
||||||
|
wire [aw-1:0] mem_adr;
|
||||||
|
assign mem_adr = (i_wen==1'b1) ? i_waddr :
|
||||||
|
i_raddr;
|
||||||
|
|
||||||
// Second interface
|
// Second port wishbone
|
||||||
wire wen_b;
|
wire [31:0] wb_adr;
|
||||||
wire [7:0] wdata_b;
|
wire [31:0] wb_dat;
|
||||||
wire [aw-1:0] waddr_b;
|
reg [31:0] wb_rdt;
|
||||||
reg [7:0] rdata_b;
|
wire [3:0] wb_sel;
|
||||||
wire [aw-1:0] raddr_b;
|
wire wb_cyc;
|
||||||
jtag_mem_protocol #(
|
wire wb_we;
|
||||||
.aw(aw),
|
wire wb_stb;
|
||||||
.chain(1)
|
reg wb_ack;
|
||||||
) jtag_mem (
|
reg wb_req_d;
|
||||||
|
wire cmd_reset;
|
||||||
|
// Driven by JTAG
|
||||||
|
jtag_wb_bridge #(
|
||||||
|
.chain(1),
|
||||||
|
.byte_aligned(1)
|
||||||
|
) jtag_wb (
|
||||||
.i_clk(i_clk),
|
.i_clk(i_clk),
|
||||||
.i_rst(1'b0),
|
.i_rst(i_rst),
|
||||||
.i_mem_rdata(rdata_b),
|
|
||||||
.o_mem_wen(wen_b),
|
.o_wb_adr(wb_adr),
|
||||||
.o_mem_wdata(wdata_b),
|
.o_wb_dat(wb_dat),
|
||||||
.o_mem_waddr(waddr_b),
|
.o_wb_sel(wb_sel),
|
||||||
.o_mem_raddr(raddr_b),
|
.o_wb_we(wb_we),
|
||||||
.o_core_reset(o_core_reset)
|
.o_wb_cyc(wb_cyc),
|
||||||
|
.o_wb_stb(wb_stb),
|
||||||
|
.i_wb_rdt(wb_rdt),
|
||||||
|
.i_wb_ack(wb_ack),
|
||||||
|
.o_cmd_reset(cmd_reset)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assign o_core_reset = cmd_reset;
|
||||||
|
|
||||||
// Read/Write
|
// Read/Write
|
||||||
always @(posedge i_clk) begin
|
always @(posedge i_clk) begin
|
||||||
// main interface
|
if (i_rst) begin
|
||||||
|
wb_req_d <= 1'b0;
|
||||||
|
wb_ack <= 1'b0;
|
||||||
|
wb_rdt <= 32'h00000000;
|
||||||
|
o_rdata <= 32'h00000000;
|
||||||
|
end else begin
|
||||||
if (i_wen)
|
if (i_wen)
|
||||||
mem[i_waddr] <= i_wdata;
|
mem[mem_adr] <= i_wdata;
|
||||||
o_rdata <= mem[i_raddr];
|
o_rdata <= mem[mem_adr];
|
||||||
// second interface
|
|
||||||
if (wen_b)
|
wb_req_d <= wb_stb && wb_cyc;
|
||||||
mem[waddr_b] <= wdata_b;
|
wb_ack <= wb_req_d;
|
||||||
rdata_b <= mem[raddr_b];
|
if (wb_we && wb_stb && wb_cyc)
|
||||||
|
mem[wb_adr[aw-1:0]] <= wb_dat[7:0];
|
||||||
|
wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]};
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Preload memory
|
// Preload memory
|
||||||
@@ -411,4 +267,6 @@ module memory #(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@@ -12,120 +12,64 @@ module top_jtag(
|
|||||||
wire clk_100;
|
wire clk_100;
|
||||||
wire clk_15;
|
wire clk_15;
|
||||||
assign clk_100 = aclk;
|
assign clk_100 = aclk;
|
||||||
clk_gen clocking(
|
clk_gen clk(
|
||||||
.clk_in(clk_100),
|
.clk_in(clk_100),
|
||||||
.clk_out_15(clk_15)
|
.clk_out_15(clk_15)
|
||||||
);
|
);
|
||||||
|
|
||||||
localparam integer JTAG_DATA_BITS = 8;
|
wire i_rst;
|
||||||
wire [JTAG_DATA_BITS-1:0] jtag_data;
|
assign i_rst = !aresetn;
|
||||||
|
|
||||||
jtag_write_reg #(
|
wire [31:0] wb_adr;
|
||||||
.DATA_BITS(JTAG_DATA_BITS),
|
wire [31:0] wb_dat;
|
||||||
.CHAIN(1)
|
wire [31:0] wb_rdt;
|
||||||
) jtag_writer (
|
wire [3:0] wb_sel;
|
||||||
.clk_i(clk_15),
|
wire wb_cyc;
|
||||||
.rst_n_i(aresetn),
|
wire wb_we;
|
||||||
.data_o(jtag_data)
|
wire wb_stb;
|
||||||
|
wire wb_ack;
|
||||||
|
wire cmd_reset;
|
||||||
|
|
||||||
|
jtag_wb_bridge #(
|
||||||
|
.chain(1)
|
||||||
|
) jtag_wb (
|
||||||
|
.i_clk(clk_15),
|
||||||
|
.i_rst(!aresetn),
|
||||||
|
|
||||||
|
.o_wb_adr(wb_adr),
|
||||||
|
.o_wb_dat(wb_dat),
|
||||||
|
.o_wb_sel(wb_sel),
|
||||||
|
.o_wb_we(wb_we),
|
||||||
|
.o_wb_cyc(wb_cyc),
|
||||||
|
.o_wb_stb(wb_stb),
|
||||||
|
.i_wb_rdt(wb_rdt),
|
||||||
|
.i_wb_ack(wb_ack),
|
||||||
|
.o_cmd_reset(cmd_reset)
|
||||||
);
|
);
|
||||||
|
|
||||||
assign LED = jtag_data[7:0];
|
wire [31:0] gpio;
|
||||||
assign led_green = jtag_data[0];
|
wire [31:0] gpio_in;
|
||||||
assign led_red = jtag_data[7];
|
assign gpio_in = 32'h0;
|
||||||
assign r2r = jtag_data[5:0];
|
|
||||||
|
|
||||||
endmodule
|
wb_gpio #(
|
||||||
|
.address(32'h00000000)
|
||||||
module jtag_write_reg #(
|
) u_wb_gpio (
|
||||||
parameter integer DATA_BITS = 8,
|
.i_wb_clk(clk_15),
|
||||||
parameter integer CHAIN = 1
|
.i_wb_rst(i_rst | cmd_reset),
|
||||||
)(
|
.i_wb_adr(wb_adr),
|
||||||
input wire clk_i,
|
.i_wb_dat(wb_dat),
|
||||||
input wire rst_n_i,
|
.i_wb_sel(wb_sel),
|
||||||
output reg [DATA_BITS-1:0] data_o
|
.i_wb_we(wb_we),
|
||||||
);
|
.i_wb_stb(wb_stb & wb_cyc),
|
||||||
wire jtag_tck;
|
.i_gpio(gpio_in),
|
||||||
wire jtag_tdi;
|
.o_wb_rdt(wb_rdt),
|
||||||
wire jtag_drck;
|
.o_wb_ack(wb_ack),
|
||||||
wire jtag_capture;
|
.o_gpio(gpio)
|
||||||
wire jtag_shift;
|
|
||||||
wire jtag_update;
|
|
||||||
wire jtag_runtest;
|
|
||||||
wire jtag_reset;
|
|
||||||
wire jtag_sel;
|
|
||||||
reg [DATA_BITS-1:0] shift_q;
|
|
||||||
reg [DATA_BITS-1:0] data_jtag_q;
|
|
||||||
reg update_toggle_jtag_q;
|
|
||||||
reg update_toggle_meta_q;
|
|
||||||
reg update_toggle_sync_q;
|
|
||||||
reg update_toggle_sync_d_q;
|
|
||||||
reg [DATA_BITS-1:0] data_meta_q;
|
|
||||||
reg [DATA_BITS-1:0] data_sync_q;
|
|
||||||
|
|
||||||
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)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
always @(posedge jtag_drck or posedge jtag_reset or negedge rst_n_i) begin
|
assign LED = gpio[7:0];
|
||||||
if (!rst_n_i || jtag_reset) begin
|
assign r2r = gpio[13:8];
|
||||||
shift_q <= {DATA_BITS{1'b0}};
|
assign led_green = gpio[30];
|
||||||
end else if (jtag_sel && jtag_capture) begin
|
assign led_red = gpio[31];
|
||||||
shift_q <= data_jtag_q;
|
|
||||||
end else if (jtag_sel && jtag_shift) begin
|
|
||||||
if (DATA_BITS == 1) begin
|
|
||||||
shift_q <= jtag_tdi;
|
|
||||||
end else begin
|
|
||||||
shift_q <= {jtag_tdi, shift_q[DATA_BITS-1:1]};
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
always @(posedge jtag_update or posedge jtag_reset or negedge rst_n_i) begin
|
|
||||||
if (!rst_n_i || jtag_reset) begin
|
|
||||||
data_jtag_q <= {DATA_BITS{1'b0}};
|
|
||||||
update_toggle_jtag_q <= 1'b0;
|
|
||||||
end else if (jtag_sel) begin
|
|
||||||
data_jtag_q <= shift_q;
|
|
||||||
update_toggle_jtag_q <= ~update_toggle_jtag_q;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// CDC into clk_i domain: toggle synchronize + stable data sampling.
|
|
||||||
always @(posedge clk_i or negedge rst_n_i) begin
|
|
||||||
if (!rst_n_i) begin
|
|
||||||
update_toggle_meta_q <= 1'b0;
|
|
||||||
update_toggle_sync_q <= 1'b0;
|
|
||||||
update_toggle_sync_d_q <= 1'b0;
|
|
||||||
data_meta_q <= {DATA_BITS{1'b0}};
|
|
||||||
data_sync_q <= {DATA_BITS{1'b0}};
|
|
||||||
data_o <= {DATA_BITS{1'b0}};
|
|
||||||
end else begin
|
|
||||||
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;
|
|
||||||
data_meta_q <= data_jtag_q;
|
|
||||||
data_sync_q <= data_meta_q;
|
|
||||||
|
|
||||||
if (update_toggle_sync_q ^ update_toggle_sync_d_q) begin
|
|
||||||
data_o <= data_sync_q;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// Not used for this simple write-register, but kept for completeness.
|
|
||||||
wire _unused_tck;
|
|
||||||
wire _unused_runtest;
|
|
||||||
assign _unused_tck = jtag_tck;
|
|
||||||
assign _unused_runtest = jtag_runtest;
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
196
rtl/wb/jtag_wb_bridge.v
Normal file
196
rtl/wb/jtag_wb_bridge.v
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module jtag_wb_bridge #(
|
||||||
|
parameter integer chain = 1,
|
||||||
|
// 0: Use cmd_addr[1:0] to select byte lane on 32-bit WB data bus.
|
||||||
|
// 1: Always use lane 0 (LSB), for byte-wide memories that return data in [7:0].
|
||||||
|
parameter integer byte_aligned = 0
|
||||||
|
)(
|
||||||
|
input wire i_clk,
|
||||||
|
input wire i_rst,
|
||||||
|
|
||||||
|
output wire [31:0] o_wb_adr,
|
||||||
|
output wire [31:0] o_wb_dat,
|
||||||
|
output wire [3:0] o_wb_sel,
|
||||||
|
output wire o_wb_we,
|
||||||
|
output wire o_wb_cyc,
|
||||||
|
output wire o_wb_stb,
|
||||||
|
input wire [31:0] i_wb_rdt,
|
||||||
|
input wire i_wb_ack,
|
||||||
|
|
||||||
|
output wire o_cmd_reset
|
||||||
|
);
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
reg [41:0] jtag_q;
|
||||||
|
wire [41:0] jtag_data_in;
|
||||||
|
wire jtag_async_reset;
|
||||||
|
|
||||||
|
jtag_if #(
|
||||||
|
.chain(chain)
|
||||||
|
) jtag (
|
||||||
|
.i_tdo(jtag_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)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign jtag_async_reset = jtag_reset || i_rst;
|
||||||
|
|
||||||
|
// JTAG shift register behavior
|
||||||
|
always @(posedge jtag_drck or posedge jtag_async_reset) begin
|
||||||
|
if (jtag_async_reset) begin
|
||||||
|
jtag_q <= 42'b0;
|
||||||
|
end else if (jtag_sel && jtag_capture) begin
|
||||||
|
jtag_q <= jtag_data_in;
|
||||||
|
end else if (jtag_sel && jtag_shift) begin
|
||||||
|
jtag_q <= {jtag_tdi, jtag_q[41:1]};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// JTAG -> i_clk crossing using toggle request/ack handshake.
|
||||||
|
// Command packet format: [41]=we, [40]=reset, [39:8]=addr, [7:0]=wdata
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
reg [41:0] j_cmd_hold;
|
||||||
|
reg j_req_tgl;
|
||||||
|
reg j_ack_sync_1;
|
||||||
|
reg j_ack_sync_2;
|
||||||
|
|
||||||
|
reg s_ack_tgl;
|
||||||
|
reg s_req_sync_1;
|
||||||
|
reg s_req_sync_2;
|
||||||
|
reg s_req_sync_3;
|
||||||
|
reg [41:0] s_cmd_sync_1;
|
||||||
|
reg [41:0] s_cmd_sync_2;
|
||||||
|
|
||||||
|
always @(posedge jtag_drck or posedge jtag_async_reset) begin
|
||||||
|
if (jtag_async_reset) begin
|
||||||
|
j_ack_sync_1 <= 1'b0;
|
||||||
|
j_ack_sync_2 <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
j_ack_sync_1 <= s_ack_tgl;
|
||||||
|
j_ack_sync_2 <= j_ack_sync_1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge jtag_update or posedge jtag_async_reset) begin
|
||||||
|
if (jtag_async_reset) begin
|
||||||
|
j_cmd_hold <= 42'b0;
|
||||||
|
j_req_tgl <= 1'b0;
|
||||||
|
end else if (jtag_sel && (j_ack_sync_2 == j_req_tgl)) begin
|
||||||
|
j_cmd_hold <= jtag_q;
|
||||||
|
j_req_tgl <= ~j_req_tgl;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Wishbone classic single-request master (1 outstanding transaction max).
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
reg wb_busy;
|
||||||
|
reg [31:0] wb_adr_r;
|
||||||
|
reg [31:0] wb_dat_r;
|
||||||
|
reg [3:0] wb_sel_r;
|
||||||
|
reg wb_we_r;
|
||||||
|
reg cmd_reset_pulse_r;
|
||||||
|
reg [31:0] resp_addr_r;
|
||||||
|
reg [7:0] resp_data_r;
|
||||||
|
|
||||||
|
wire req_pulse;
|
||||||
|
wire [7:0] cmd_wdata;
|
||||||
|
wire [31:0] cmd_addr;
|
||||||
|
wire cmd_reset;
|
||||||
|
wire cmd_we;
|
||||||
|
wire [1:0] req_lane;
|
||||||
|
wire [1:0] resp_lane;
|
||||||
|
|
||||||
|
assign req_pulse = s_req_sync_2 ^ s_req_sync_3;
|
||||||
|
assign cmd_wdata = s_cmd_sync_2[7:0];
|
||||||
|
assign cmd_addr = s_cmd_sync_2[39:8];
|
||||||
|
assign cmd_reset = s_cmd_sync_2[40];
|
||||||
|
assign cmd_we = s_cmd_sync_2[41];
|
||||||
|
assign req_lane = byte_aligned ? 2'b00 : cmd_addr[1:0];
|
||||||
|
assign resp_lane = byte_aligned ? 2'b00 : wb_adr_r[1:0];
|
||||||
|
|
||||||
|
assign o_wb_adr = wb_adr_r;
|
||||||
|
assign o_wb_dat = wb_dat_r;
|
||||||
|
assign o_wb_sel = wb_sel_r;
|
||||||
|
assign o_wb_we = wb_we_r;
|
||||||
|
assign o_wb_cyc = wb_busy;
|
||||||
|
assign o_wb_stb = wb_busy;
|
||||||
|
assign o_cmd_reset = cmd_reset_pulse_r;
|
||||||
|
|
||||||
|
always @(posedge i_clk) begin
|
||||||
|
if (i_rst) begin
|
||||||
|
s_ack_tgl <= 1'b0;
|
||||||
|
s_req_sync_1 <= 1'b0;
|
||||||
|
s_req_sync_2 <= 1'b0;
|
||||||
|
s_req_sync_3 <= 1'b0;
|
||||||
|
s_cmd_sync_1 <= 42'b0;
|
||||||
|
s_cmd_sync_2 <= 42'b0;
|
||||||
|
wb_busy <= 1'b0;
|
||||||
|
wb_adr_r <= 32'b0;
|
||||||
|
wb_dat_r <= 32'b0;
|
||||||
|
wb_sel_r <= 4'b0000;
|
||||||
|
wb_we_r <= 1'b0;
|
||||||
|
cmd_reset_pulse_r <= 1'b0;
|
||||||
|
resp_addr_r <= 32'b0;
|
||||||
|
resp_data_r <= 8'b0;
|
||||||
|
end else begin
|
||||||
|
s_req_sync_1 <= j_req_tgl;
|
||||||
|
s_req_sync_2 <= s_req_sync_1;
|
||||||
|
s_req_sync_3 <= s_req_sync_2;
|
||||||
|
s_cmd_sync_1 <= j_cmd_hold;
|
||||||
|
s_cmd_sync_2 <= s_cmd_sync_1;
|
||||||
|
cmd_reset_pulse_r <= 1'b0;
|
||||||
|
|
||||||
|
if (req_pulse && !wb_busy) begin
|
||||||
|
wb_busy <= 1'b1;
|
||||||
|
wb_we_r <= cmd_we;
|
||||||
|
wb_adr_r <= cmd_addr;
|
||||||
|
|
||||||
|
case (req_lane)
|
||||||
|
2'b00: begin wb_sel_r <= 4'b0001; wb_dat_r <= {24'b0, cmd_wdata}; end
|
||||||
|
2'b01: begin wb_sel_r <= 4'b0010; wb_dat_r <= {16'b0, cmd_wdata, 8'b0}; end
|
||||||
|
2'b10: begin wb_sel_r <= 4'b0100; wb_dat_r <= {8'b0, cmd_wdata, 16'b0}; end
|
||||||
|
default: begin wb_sel_r <= 4'b1000; wb_dat_r <= {cmd_wdata, 24'b0}; end
|
||||||
|
endcase
|
||||||
|
|
||||||
|
cmd_reset_pulse_r <= cmd_reset;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (wb_busy && i_wb_ack) begin
|
||||||
|
wb_busy <= 1'b0;
|
||||||
|
wb_we_r <= 1'b0;
|
||||||
|
resp_addr_r <= wb_adr_r;
|
||||||
|
|
||||||
|
case (resp_lane)
|
||||||
|
2'b00: resp_data_r <= i_wb_rdt[7:0];
|
||||||
|
2'b01: resp_data_r <= i_wb_rdt[15:8];
|
||||||
|
2'b10: resp_data_r <= i_wb_rdt[23:16];
|
||||||
|
default: resp_data_r <= i_wb_rdt[31:24];
|
||||||
|
endcase
|
||||||
|
|
||||||
|
s_ack_tgl <= s_req_sync_2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign jtag_data_in = {2'b00, resp_addr_r, resp_data_r};
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -5,21 +5,6 @@ STATE RESET;
|
|||||||
STATE IDLE;
|
STATE IDLE;
|
||||||
SIR 6 TDI (02);
|
SIR 6 TDI (02);
|
||||||
|
|
||||||
SDR 8 TDI (07);
|
SDR 42 TDI (3A987654321);
|
||||||
SDR 8 TDI (4C);
|
|
||||||
SDR 8 TDI (43);
|
|
||||||
SDR 8 TDI (D1);
|
|
||||||
SDR 8 TDI (97);
|
|
||||||
SDR 8 TDI (6B);
|
|
||||||
SDR 8 TDI (3D);
|
|
||||||
SDR 8 TDI (E4);
|
|
||||||
SDR 8 TDI (20);
|
|
||||||
SDR 8 TDI (3A);
|
|
||||||
SDR 8 TDI (8E);
|
|
||||||
SDR 8 TDI (1D);
|
|
||||||
SDR 8 TDI (9D);
|
|
||||||
SDR 8 TDI (E4);
|
|
||||||
|
|
||||||
RUNTEST 16 TCK;
|
|
||||||
STATE RESET;
|
|
||||||
STATE IDLE;
|
STATE IDLE;
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
`timescale 1ns/1ps
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
module tb_svf();
|
module tb_svf();
|
||||||
|
reg clk;
|
||||||
|
reg resetn;
|
||||||
|
initial clk <= 1'b0;
|
||||||
|
initial resetn <= 1'b0;
|
||||||
|
always #33.33 clk <= !clk;
|
||||||
|
initial #40 resetn <= 1'b1;
|
||||||
|
|
||||||
wire [7:0] led_out;
|
wire [7:0] led_out;
|
||||||
wire [31:0] update_count;
|
|
||||||
|
|
||||||
jtag_byte_sink #(
|
jtag_byte_sink #(
|
||||||
.SVF_FILE("sim/other/test.svf"),
|
.SVF_FILE("sim/other/test.svf"),
|
||||||
.TCK_HALF_PERIOD_NS(50)
|
.TCK_HALF_PERIOD_NS(500)
|
||||||
) dut (
|
) dut (
|
||||||
.o_led(led_out),
|
.i_clk(clk),
|
||||||
.o_update_count(update_count)
|
.i_rst(!resetn),
|
||||||
|
.o_led(led_out)
|
||||||
);
|
);
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
// Optional waveform dump.
|
|
||||||
$dumpfile("out.vcd");
|
$dumpfile("out.vcd");
|
||||||
$dumpvars;
|
$dumpvars;
|
||||||
|
#200_000;
|
||||||
// Timeout for large SVF playback.
|
|
||||||
#20_000;
|
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
@@ -27,9 +31,14 @@ module jtag_byte_sink #(
|
|||||||
parameter [8*256-1:0] SVF_FILE = "",
|
parameter [8*256-1:0] SVF_FILE = "",
|
||||||
parameter integer TCK_HALF_PERIOD_NS = 50
|
parameter integer TCK_HALF_PERIOD_NS = 50
|
||||||
)(
|
)(
|
||||||
output reg [7:0] o_led,
|
input wire i_clk,
|
||||||
output reg [31:0] o_update_count
|
input wire i_rst,
|
||||||
|
output reg [7:0] o_led
|
||||||
);
|
);
|
||||||
|
|
||||||
|
initial o_led <= 0;
|
||||||
|
|
||||||
|
// JTAG interface wires
|
||||||
wire jtag_tck;
|
wire jtag_tck;
|
||||||
wire jtag_tdi;
|
wire jtag_tdi;
|
||||||
wire jtag_drck;
|
wire jtag_drck;
|
||||||
@@ -39,10 +48,10 @@ module jtag_byte_sink #(
|
|||||||
wire jtag_runtest;
|
wire jtag_runtest;
|
||||||
wire jtag_reset;
|
wire jtag_reset;
|
||||||
wire jtag_sel;
|
wire jtag_sel;
|
||||||
wire jtag_tdo;
|
|
||||||
reg [7:0] shift_reg;
|
|
||||||
|
|
||||||
assign jtag_tdo = shift_reg[0];
|
reg [41:0] jtag_q;
|
||||||
|
reg [41:0] jtag_data;
|
||||||
|
wire jtag_async_reset;
|
||||||
|
|
||||||
jtag_if #(
|
jtag_if #(
|
||||||
.chain(1),
|
.chain(1),
|
||||||
@@ -50,7 +59,7 @@ module jtag_byte_sink #(
|
|||||||
.TCK_HALF_PERIOD_NS(TCK_HALF_PERIOD_NS),
|
.TCK_HALF_PERIOD_NS(TCK_HALF_PERIOD_NS),
|
||||||
.USER_IR_OPCODE(32'h0000_0002)
|
.USER_IR_OPCODE(32'h0000_0002)
|
||||||
) jtag (
|
) jtag (
|
||||||
.i_tdo(jtag_tdo),
|
.i_tdo(jtag_q[0]),
|
||||||
.o_tck(jtag_tck),
|
.o_tck(jtag_tck),
|
||||||
.o_tdi(jtag_tdi),
|
.o_tdi(jtag_tdi),
|
||||||
.o_drck(jtag_drck),
|
.o_drck(jtag_drck),
|
||||||
@@ -62,29 +71,36 @@ module jtag_byte_sink #(
|
|||||||
.o_sel(jtag_sel)
|
.o_sel(jtag_sel)
|
||||||
);
|
);
|
||||||
|
|
||||||
always @(posedge jtag_drck or posedge jtag_reset) begin
|
|
||||||
if (jtag_reset) begin
|
assign jtag_async_reset = jtag_reset || i_rst;
|
||||||
shift_reg <= 8'h00;
|
|
||||||
|
always @(posedge jtag_drck or posedge jtag_async_reset) begin
|
||||||
|
if (jtag_async_reset) begin
|
||||||
|
jtag_q <= 0;
|
||||||
end else if (jtag_sel && jtag_capture) begin
|
end else if (jtag_sel && jtag_capture) begin
|
||||||
shift_reg <= o_led;
|
jtag_q <= jtag_data;
|
||||||
end else if (jtag_sel && jtag_shift) begin
|
end else if (jtag_sel && jtag_shift) begin
|
||||||
shift_reg <= {jtag_tdi, shift_reg[7:1]};
|
jtag_q <= {jtag_tdi, jtag_q[41:1]};
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
always @(posedge jtag_update or posedge jtag_reset) begin
|
always @(posedge jtag_update or posedge jtag_async_reset) begin
|
||||||
if (jtag_reset) begin
|
if (jtag_async_reset) begin
|
||||||
o_led <= 8'h00;
|
jtag_data <= 0;
|
||||||
o_update_count <= 32'd0;
|
|
||||||
end else if (jtag_sel) begin
|
end else if (jtag_sel) begin
|
||||||
o_led <= shift_reg;
|
jtag_data <= jtag_q;
|
||||||
o_update_count <= o_update_count + 1'b1;
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Keep lint happy for currently unused outputs.
|
wire [41:0] j_data;
|
||||||
wire _unused_tck;
|
wire j_data_update;
|
||||||
wire _unused_runtest;
|
cdc_strobed #(42) j_data_cdc (
|
||||||
assign _unused_tck = jtag_tck;
|
.i_clk_a(i_clk),
|
||||||
assign _unused_runtest = jtag_runtest;
|
.i_clk_b(i_clk),
|
||||||
|
.i_data(jtag_data),
|
||||||
|
.i_strobe(jtag_update),
|
||||||
|
.o_data(j_data),
|
||||||
|
.o_strobe(j_data_update)
|
||||||
|
);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ CC := $(TOOLCHAIN_PREFIX)g++
|
|||||||
|
|
||||||
TARGET := test
|
TARGET := test
|
||||||
SRCS_C :=
|
SRCS_C :=
|
||||||
SRCS_CPP:= test.cpp digilent_jtag.cpp
|
SRCS_CPP:= test.cpp digilent_jtag.cpp argparse.cpp
|
||||||
OBJS := $(SRCS_C:.c=.o) $(SRCS_CPP:.cpp=.o)
|
OBJS := $(SRCS_C:.c=.o) $(SRCS_CPP:.cpp=.o)
|
||||||
|
|
||||||
ADEPT_LIBDIR := /opt/packages/digilent.adept.runtime_2.27.9-x86_64/lib64
|
ADEPT_LIBDIR := /opt/packages/digilent.adept.runtime_2.27.9-x86_64/lib64
|
||||||
|
|||||||
158
tools/argparse.cpp
Normal file
158
tools/argparse.cpp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#include "argparse.hpp"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
ArgParser::ArgParser(std::string program_name)
|
||||||
|
: program_name_(std::move(program_name)) {}
|
||||||
|
|
||||||
|
void ArgParser::addString(const std::string &name,
|
||||||
|
const std::string &default_value,
|
||||||
|
const std::string &help,
|
||||||
|
bool required) {
|
||||||
|
order_.push_back(name);
|
||||||
|
meta_[name] = {OptionType::kString, help, required};
|
||||||
|
string_values_[name] = default_value;
|
||||||
|
provided_[name] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgParser::addInt(const std::string &name,
|
||||||
|
int default_value,
|
||||||
|
const std::string &help,
|
||||||
|
bool required) {
|
||||||
|
order_.push_back(name);
|
||||||
|
meta_[name] = {OptionType::kInt, help, required};
|
||||||
|
int_values_[name] = default_value;
|
||||||
|
provided_[name] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArgParser::parse(int argc, char **argv, std::string *error) {
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string token(argv[i]);
|
||||||
|
if (token == "--help" || token == "-h") {
|
||||||
|
if (error) {
|
||||||
|
*error = "help";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.rfind("--", 0) != 0) {
|
||||||
|
if (error) {
|
||||||
|
*error = "Unexpected positional argument: " + token;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key;
|
||||||
|
std::string value;
|
||||||
|
size_t eq = token.find('=');
|
||||||
|
if (eq == std::string::npos) {
|
||||||
|
key = token.substr(2);
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
if (error) {
|
||||||
|
*error = "Missing value for --" + key;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = argv[++i];
|
||||||
|
} else {
|
||||||
|
key = token.substr(2, eq - 2);
|
||||||
|
value = token.substr(eq + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto m = meta_.find(key);
|
||||||
|
if (m == meta_.end()) {
|
||||||
|
if (error) {
|
||||||
|
*error = "Unknown option: --" + key;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m->second.type == OptionType::kString) {
|
||||||
|
string_values_[key] = value;
|
||||||
|
provided_[key] = true;
|
||||||
|
} else {
|
||||||
|
errno = 0;
|
||||||
|
char *endp = nullptr;
|
||||||
|
long parsed = std::strtol(value.c_str(), &endp, 0);
|
||||||
|
if (errno != 0 || endp == value.c_str() || *endp != '\0' ||
|
||||||
|
parsed < INT_MIN || parsed > INT_MAX) {
|
||||||
|
if (error) {
|
||||||
|
*error = "Invalid integer for --" + key + ": " + value;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int_values_[key] = static_cast<int>(parsed);
|
||||||
|
provided_[key] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &key : order_) {
|
||||||
|
auto m = meta_.find(key);
|
||||||
|
if (m != meta_.end() && m->second.required && !has(key)) {
|
||||||
|
if (error) {
|
||||||
|
*error = "Missing required option: --" + key;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArgParser::has(const std::string &name) const {
|
||||||
|
auto p = provided_.find(name);
|
||||||
|
return p != provided_.end() && p->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ArgParser::getString(const std::string &name) const {
|
||||||
|
auto it = string_values_.find(name);
|
||||||
|
if (it == string_values_.end()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ArgParser::getInt(const std::string &name) const {
|
||||||
|
auto it = int_values_.find(name);
|
||||||
|
if (it == int_values_.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ArgParser::helpText() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Usage: " << program_name_ << " [options]\n\n";
|
||||||
|
oss << "Options:\n";
|
||||||
|
oss << " -h, --help Show this help\n";
|
||||||
|
for (const auto &key : order_) {
|
||||||
|
auto m = meta_.find(key);
|
||||||
|
if (m == meta_.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << " --" << key << " <value>";
|
||||||
|
if (m->second.required) {
|
||||||
|
oss << " (required)";
|
||||||
|
}
|
||||||
|
oss << "\n";
|
||||||
|
oss << " " << m->second.help;
|
||||||
|
if (m->second.type == OptionType::kString) {
|
||||||
|
auto s = string_values_.find(key);
|
||||||
|
if (s != string_values_.end()) {
|
||||||
|
oss << " [default: '" << s->second << "']";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto iv = int_values_.find(key);
|
||||||
|
if (iv != int_values_.end()) {
|
||||||
|
oss << " [default: " << iv->second << "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oss << "\n";
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
63
tools/argparse.hpp
Normal file
63
tools/argparse.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class ArgParser {
|
||||||
|
public:
|
||||||
|
struct StringOption {
|
||||||
|
std::string name;
|
||||||
|
std::string default_value;
|
||||||
|
std::string help;
|
||||||
|
bool required;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntOption {
|
||||||
|
std::string name;
|
||||||
|
int default_value;
|
||||||
|
std::string help;
|
||||||
|
bool required;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ArgParser(std::string program_name);
|
||||||
|
|
||||||
|
void addString(const std::string &name,
|
||||||
|
const std::string &default_value,
|
||||||
|
const std::string &help,
|
||||||
|
bool required = false);
|
||||||
|
|
||||||
|
void addInt(const std::string &name,
|
||||||
|
int default_value,
|
||||||
|
const std::string &help,
|
||||||
|
bool required = false);
|
||||||
|
|
||||||
|
bool parse(int argc, char **argv, std::string *error);
|
||||||
|
|
||||||
|
bool has(const std::string &name) const;
|
||||||
|
std::string getString(const std::string &name) const;
|
||||||
|
int getInt(const std::string &name) const;
|
||||||
|
|
||||||
|
std::string helpText() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class OptionType {
|
||||||
|
kString,
|
||||||
|
kInt
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptionMeta {
|
||||||
|
OptionType type;
|
||||||
|
std::string help;
|
||||||
|
bool required;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string program_name_;
|
||||||
|
std::vector<std::string> order_;
|
||||||
|
std::unordered_map<std::string, OptionMeta> meta_;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> string_values_;
|
||||||
|
std::unordered_map<std::string, int> int_values_;
|
||||||
|
std::unordered_map<std::string, bool> provided_;
|
||||||
|
};
|
||||||
@@ -1,18 +1,89 @@
|
|||||||
#include "digilent_jtag.hpp"
|
#include "digilent_jtag.hpp"
|
||||||
|
#include "argparse.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void write(DigilentJtag &jtag, uint32_t addr, uint8_t data, bool reset){
|
||||||
|
uint8_t d[6], din[6];
|
||||||
|
d[0] = data;
|
||||||
|
d[1] = (uint8_t)addr;
|
||||||
|
d[2] = (uint8_t)(addr>>8);
|
||||||
|
d[3] = (uint8_t)(addr>>16);
|
||||||
|
d[4] = (uint8_t)(addr>>24);
|
||||||
|
d[5] = ((reset) ? 1 : 0) | 0x2; // <we><rst>
|
||||||
|
|
||||||
|
jtag.shiftData(d, din, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t read(DigilentJtag &jtag, uint32_t addr, bool reset){
|
||||||
|
uint8_t d[6], din[6];
|
||||||
|
d[0] = 0xff;
|
||||||
|
d[1] = (uint8_t)addr;
|
||||||
|
d[2] = (uint8_t)(addr>>8);
|
||||||
|
d[3] = (uint8_t)(addr>>16);
|
||||||
|
d[4] = (uint8_t)(addr>>24);
|
||||||
|
d[5] = ((reset) ? 1 : 0); // <we><rst>
|
||||||
|
|
||||||
|
jtag.shiftData(d, din, 42);
|
||||||
|
// Read back
|
||||||
|
jtag.shiftData(d, din, 42);
|
||||||
|
|
||||||
|
return din[0];
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv){
|
int main(int argc, char** argv){
|
||||||
|
ArgParser parser(argc > 0 ? argv[0] : "test");
|
||||||
|
parser.addString("write", "", "file to write");
|
||||||
|
|
||||||
|
std::string parse_error;
|
||||||
|
if (!parser.parse(argc, argv, &parse_error)) {
|
||||||
|
if (parse_error == "help") {
|
||||||
|
std::printf("%s", parser.helpText().c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::printf("Argument error: %s\n\n", parse_error.c_str());
|
||||||
|
std::printf("%s", parser.helpText().c_str());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string arg_write = parser.getString("write");
|
||||||
|
|
||||||
DigilentJtag jtag;
|
DigilentJtag jtag;
|
||||||
if(!jtag.open()){
|
if(!jtag.open()){
|
||||||
printf("Could not open programmer\r\n");
|
printf("Could not open programmer\r\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
jtag.setChain(1);
|
jtag.setChain(1);
|
||||||
|
|
||||||
uint8_t data_out = 0xAB;
|
// Start reset
|
||||||
uint8_t data_in;
|
read(jtag, 0, true);
|
||||||
jtag.shiftData(&data_out, &data_in, 8);
|
|
||||||
|
if(arg_write!=""){
|
||||||
|
uint32_t addr = 0;
|
||||||
|
uint8_t buf[32];
|
||||||
|
int nr;
|
||||||
|
FILE* f = fopen(arg_write.c_str(), "rb");
|
||||||
|
if(!f){
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
do{
|
||||||
|
nr = fread(buf, 1, 32, f);
|
||||||
|
for(int i=0; i<32; i++){
|
||||||
|
write(jtag, addr, buf[i], true);
|
||||||
|
addr++;
|
||||||
|
}
|
||||||
|
}while(nr>0);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
end:
|
||||||
|
// End reset
|
||||||
|
read(jtag, 0, false);
|
||||||
|
|
||||||
jtag.close();
|
jtag.close();
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user