Added JTAG interface with testbench
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
out
|
||||
build
|
||||
env
|
||||
env
|
||||
__pycache*
|
||||
_impactbatch.log
|
||||
@@ -24,4 +24,29 @@ NET "r2r[1]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[2]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[3]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[4]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[5]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[5]" IOSTANDARD = LVCMOS33;
|
||||
|
||||
NET "LED[0]" LOC = P119;
|
||||
NET "LED[0]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[0]" DRIVE = 8;
|
||||
NET "LED[1]" LOC = P118;
|
||||
NET "LED[1]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[1]" DRIVE = 8;
|
||||
NET "LED[2]" LOC = P117;
|
||||
NET "LED[2]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[2]" DRIVE = 8;
|
||||
NET "LED[3]" LOC = P116;
|
||||
NET "LED[3]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[3]" DRIVE = 8;
|
||||
NET "LED[4]" LOC = P115;
|
||||
NET "LED[4]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[4]" DRIVE = 8;
|
||||
NET "LED[5]" LOC = P114;
|
||||
NET "LED[5]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[5]" DRIVE = 8;
|
||||
NET "LED[6]" LOC = P112;
|
||||
NET "LED[6]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[6]" DRIVE = 8;
|
||||
NET "LED[7]" LOC = P111;
|
||||
NET "LED[7]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[7]" DRIVE = 8;
|
||||
43
project.cfg
43
project.cfg
@@ -4,6 +4,15 @@ version = 0.1
|
||||
out_dir = out
|
||||
build_dir = build
|
||||
|
||||
[target.ip]
|
||||
toolchain = ISE_IP
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
family = spartan6
|
||||
device = xc6slx9
|
||||
package = tqg144
|
||||
speedgrade = -2
|
||||
files_def = boards/mimas_v1/ip/clk_gen.xco
|
||||
|
||||
[target.synth]
|
||||
toolchain = ISE
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
@@ -56,31 +65,25 @@ files_other = rtl/util/rc_alpha_q15.vh
|
||||
rtl/util/clog2.vh
|
||||
sw/blinky/blinky.hex
|
||||
|
||||
[target.ip]
|
||||
toolchain = ISE_IP
|
||||
[target.jtag]
|
||||
toolchain = ISE
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
family = spartan6
|
||||
device = xc6slx9
|
||||
package = tqg144
|
||||
speedgrade = -2
|
||||
files_def = boards/mimas_v1/ip/clk_gen.xco
|
||||
toplevel = top_jtag
|
||||
xst_opts = -vlgincdir rtl/util
|
||||
files_other =
|
||||
files_con = boards/mimas_v1/constraints.ucf
|
||||
files_verilog = rtl/arch/spartan-6/jtag_if.v
|
||||
rtl/arch/spartan-6/clk_gen.v
|
||||
rtl/toplevel/top_jtag.v
|
||||
|
||||
[target.sim]
|
||||
[target.svftest]
|
||||
toolchain = iverilog
|
||||
runtime = all
|
||||
toplevel = tb_sigmadelta
|
||||
ivl_opts = -Irtl/util
|
||||
files_verilog = sim/tb/tb_nco_q15.v
|
||||
sim/tb/tb_sigmadelta.v
|
||||
sim/tb/tb_mul_const.v
|
||||
rtl/core/nco_q15.v
|
||||
rtl/core/lvds_comparator.v
|
||||
rtl/core/sigmadelta_rcmodel_q15.v
|
||||
rtl/core/sigmadelta_input_q15.v
|
||||
rtl/core/mul_const.v
|
||||
rtl/core/lpf_iir_q15_k.v
|
||||
rtl/core/decimate_by_r_q15.v
|
||||
sim/overrides/sigmadelta_sampler.v
|
||||
sim/overrides/clk_gen.v
|
||||
files_other = rtl/util/conv.vh
|
||||
rtl/util/rc_alpha_q15.vh
|
||||
toplevel = tb_svf
|
||||
files_verilog = sim/tb/tb_svf.v
|
||||
sim/overrides/jtag_if.v
|
||||
files_other = sim/other/test.svf
|
||||
|
||||
34
rtl/arch/spartan-6/jtag_if.v
Normal file
34
rtl/arch/spartan-6/jtag_if.v
Normal file
@@ -0,0 +1,34 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// =============================================================================
|
||||
// JTAG interface
|
||||
// Spartan-6 BSCAN primitive wrapper (USER1 chain).
|
||||
// =============================================================================
|
||||
module jtag_if #(
|
||||
parameter chain = 1
|
||||
)(
|
||||
input wire i_tdo,
|
||||
output wire o_tck,
|
||||
output wire o_tdi,
|
||||
output wire o_drck,
|
||||
output wire o_capture,
|
||||
output wire o_shift,
|
||||
output wire o_update,
|
||||
output wire o_runtest,
|
||||
output wire o_reset,
|
||||
output wire o_sel
|
||||
);
|
||||
BSCAN_SPARTAN6 #(
|
||||
.JTAG_CHAIN(chain)
|
||||
) bscan_i (
|
||||
.CAPTURE(o_capture),
|
||||
.DRCK(o_drck),
|
||||
.RESET(o_reset),
|
||||
.RUNTEST(o_runtest),
|
||||
.SEL(o_sel),
|
||||
.SHIFT(o_shift),
|
||||
.TCK(o_tck),
|
||||
.TDI(o_tdi),
|
||||
.UPDATE(o_update)
|
||||
);
|
||||
endmodule
|
||||
34
rtl/core/jtag_if.v
Normal file
34
rtl/core/jtag_if.v
Normal file
@@ -0,0 +1,34 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// =============================================================================
|
||||
// JTAG interface
|
||||
// Generic stub model with inactive/tied-off outputs.
|
||||
// =============================================================================
|
||||
module jtag_if #(
|
||||
parameter chain = 1
|
||||
)(
|
||||
input wire i_tdo,
|
||||
output wire o_tck,
|
||||
output wire o_tdi,
|
||||
output wire o_drck,
|
||||
output wire o_capture,
|
||||
output wire o_shift,
|
||||
output wire o_update,
|
||||
output wire o_runtest,
|
||||
output wire o_reset,
|
||||
output wire o_sel
|
||||
);
|
||||
assign o_tck = 1'b0;
|
||||
assign o_tdi = 1'b0;
|
||||
assign o_drck = 1'b0;
|
||||
assign o_capture = 1'b0;
|
||||
assign o_shift = 1'b0;
|
||||
assign o_update = 1'b0;
|
||||
assign o_runtest = 1'b0;
|
||||
assign o_reset = 1'b0;
|
||||
assign o_sel = 1'b0;
|
||||
|
||||
// Keep lint tools quiet in generic builds where TDO is unused.
|
||||
wire _unused_tdo;
|
||||
assign _unused_tdo = i_tdo;
|
||||
endmodule
|
||||
131
rtl/toplevel/top_jtag.v
Normal file
131
rtl/toplevel/top_jtag.v
Normal file
@@ -0,0 +1,131 @@
|
||||
module top_jtag(
|
||||
input wire aclk,
|
||||
input wire aresetn,
|
||||
|
||||
output wire led_green,
|
||||
output wire led_red,
|
||||
output wire [7:0] LED,
|
||||
|
||||
output wire [5:0] r2r
|
||||
);
|
||||
// Clocking
|
||||
wire clk_100;
|
||||
wire clk_15;
|
||||
assign clk_100 = aclk;
|
||||
clk_gen clocking(
|
||||
.clk_in(clk_100),
|
||||
.clk_out_15(clk_15)
|
||||
);
|
||||
|
||||
localparam integer JTAG_DATA_BITS = 8;
|
||||
wire [JTAG_DATA_BITS-1:0] jtag_data;
|
||||
|
||||
jtag_write_reg #(
|
||||
.DATA_BITS(JTAG_DATA_BITS),
|
||||
.CHAIN(1)
|
||||
) jtag_writer (
|
||||
.clk_i(clk_15),
|
||||
.rst_n_i(aresetn),
|
||||
.data_o(jtag_data)
|
||||
);
|
||||
|
||||
assign LED = jtag_data[7:0];
|
||||
assign led_green = jtag_data[0];
|
||||
assign led_red = jtag_data[7];
|
||||
assign r2r = jtag_data[5:0];
|
||||
|
||||
endmodule
|
||||
|
||||
module jtag_write_reg #(
|
||||
parameter integer DATA_BITS = 8,
|
||||
parameter integer CHAIN = 1
|
||||
)(
|
||||
input wire clk_i,
|
||||
input wire rst_n_i,
|
||||
output reg [DATA_BITS-1:0] data_o
|
||||
);
|
||||
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 [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
|
||||
if (!rst_n_i || jtag_reset) begin
|
||||
shift_q <= {DATA_BITS{1'b0}};
|
||||
end else if (jtag_sel && jtag_capture) begin
|
||||
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
|
||||
167
scripts/jtag_write_user_impact.py
Executable file
167
scripts/jtag_write_user_impact.py
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/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())
|
||||
25
sim/other/test.svf
Normal file
25
sim/other/test.svf
Normal file
@@ -0,0 +1,25 @@
|
||||
TRST ABSENT;
|
||||
ENDIR IDLE;
|
||||
ENDDR IDLE;
|
||||
STATE RESET;
|
||||
STATE IDLE;
|
||||
SIR 6 TDI (02);
|
||||
|
||||
SDR 8 TDI (07);
|
||||
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;
|
||||
196
sim/overrides/jtag_if.v
Normal file
196
sim/overrides/jtag_if.v
Normal file
@@ -0,0 +1,196 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// =============================================================================
|
||||
// JTAG interface (simulation override)
|
||||
// Behavioral SVF player for simple USER-chain simulation.
|
||||
//
|
||||
// Supported SVF commands (line-oriented, uppercase recommended):
|
||||
// - SIR <n> TDI (<hex>);
|
||||
// - SDR <n> TDI (<hex>);
|
||||
// - RUNTEST <n> TCK;
|
||||
// - STATE RESET;
|
||||
// - STATE IDLE;
|
||||
// Other lines are ignored.
|
||||
// =============================================================================
|
||||
module jtag_if #(
|
||||
parameter integer chain = 1,
|
||||
parameter [8*256-1:0] SVF_FILE = "write_jtag.svf",
|
||||
parameter integer TCK_HALF_PERIOD_NS = 50,
|
||||
parameter [31:0] USER_IR_OPCODE = 32'h00000002
|
||||
)(
|
||||
input wire i_tdo,
|
||||
output reg o_tck,
|
||||
output reg o_tdi,
|
||||
output reg o_drck,
|
||||
output reg o_capture,
|
||||
output reg o_shift,
|
||||
output reg o_update,
|
||||
output reg o_runtest,
|
||||
output reg o_reset,
|
||||
output reg o_sel
|
||||
);
|
||||
integer fd;
|
||||
integer got;
|
||||
integer nbits;
|
||||
integer cycles;
|
||||
integer i;
|
||||
integer line_no;
|
||||
integer scan_bits;
|
||||
|
||||
reg [8*1024-1:0] line;
|
||||
reg [8*32-1:0] cmd;
|
||||
reg [8*32-1:0] arg1;
|
||||
reg [8*256-1:0] svf_file_path;
|
||||
reg [4095:0] scan_data;
|
||||
reg [31:0] current_ir;
|
||||
reg selected;
|
||||
|
||||
// Keep linters quiet: TDO is not consumed by this simple player.
|
||||
wire _unused_tdo;
|
||||
assign _unused_tdo = i_tdo;
|
||||
|
||||
task pulse_tck;
|
||||
begin
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_drck_shift;
|
||||
input bit_in;
|
||||
begin
|
||||
o_tdi = bit_in;
|
||||
o_shift = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b1;
|
||||
o_drck = 1'b1;
|
||||
end
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b0;
|
||||
o_drck = 1'b0;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_capture;
|
||||
begin
|
||||
o_capture = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b1;
|
||||
o_drck = 1'b1;
|
||||
end
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b0;
|
||||
o_drck = 1'b0;
|
||||
o_capture = 1'b0;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_update;
|
||||
begin
|
||||
o_shift = 1'b0;
|
||||
#TCK_HALF_PERIOD_NS o_update = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_update = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
// Default output levels
|
||||
o_tck = 1'b0;
|
||||
o_tdi = 1'b0;
|
||||
o_drck = 1'b0;
|
||||
o_capture = 1'b0;
|
||||
o_shift = 1'b0;
|
||||
o_update = 1'b0;
|
||||
o_runtest = 1'b0;
|
||||
o_reset = 1'b0;
|
||||
o_sel = 1'b0;
|
||||
selected = 1'b0;
|
||||
current_ir = 32'h0;
|
||||
line_no = 0;
|
||||
svf_file_path = SVF_FILE;
|
||||
|
||||
// Deterministic startup reset pulse before consuming SVF.
|
||||
o_reset = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b0;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b0;
|
||||
o_reset = 1'b0;
|
||||
|
||||
fd = $fopen(svf_file_path, "r");
|
||||
if (fd == 0) begin
|
||||
$display("jtag_if(sim): could not open SVF file '%0s'", svf_file_path);
|
||||
end else begin
|
||||
while (!$feof(fd)) begin
|
||||
line = {8*1024{1'b0}};
|
||||
got = $fgets(line, fd);
|
||||
line_no = line_no + 1;
|
||||
if (got > 0) begin
|
||||
cmd = {8*32{1'b0}};
|
||||
arg1 = {8*32{1'b0}};
|
||||
scan_data = {4096{1'b0}};
|
||||
nbits = 0;
|
||||
cycles = 0;
|
||||
|
||||
got = $sscanf(line, "%s", cmd);
|
||||
if (got < 1) begin
|
||||
// Empty line
|
||||
end else if (cmd == "!") begin
|
||||
// Comment line
|
||||
end else if (cmd == "SIR") begin
|
||||
got = $sscanf(line, "SIR %d TDI (%h);", nbits, scan_data);
|
||||
if (got == 2) begin
|
||||
current_ir = scan_data[31:0];
|
||||
selected = (current_ir == USER_IR_OPCODE);
|
||||
o_sel = selected;
|
||||
|
||||
// Emit TCK pulses for IR shift activity.
|
||||
scan_bits = nbits;
|
||||
if (scan_bits > 4096) scan_bits = 4096;
|
||||
for (i = 0; i < scan_bits; i = i + 1) begin
|
||||
o_tdi = scan_data[i];
|
||||
pulse_tck;
|
||||
end
|
||||
end
|
||||
end else if (cmd == "SDR") begin
|
||||
got = $sscanf(line, "SDR %d TDI (%h);", nbits, scan_data);
|
||||
if (got == 2) begin
|
||||
if (selected) begin
|
||||
pulse_capture;
|
||||
end
|
||||
scan_bits = nbits;
|
||||
if (scan_bits > 4096) scan_bits = 4096;
|
||||
for (i = 0; i < scan_bits; i = i + 1) begin
|
||||
pulse_drck_shift(scan_data[i]);
|
||||
end
|
||||
if (selected) begin
|
||||
pulse_update;
|
||||
end
|
||||
end
|
||||
end else if (cmd == "RUNTEST") begin
|
||||
got = $sscanf(line, "RUNTEST %d TCK;", cycles);
|
||||
if (got == 1) begin
|
||||
o_runtest = 1'b1;
|
||||
for (i = 0; i < cycles; i = i + 1)
|
||||
pulse_tck;
|
||||
o_runtest = 1'b0;
|
||||
end
|
||||
end else if (cmd == "STATE") begin
|
||||
got = $sscanf(line, "STATE %s", arg1);
|
||||
if (got == 1) begin
|
||||
if (arg1 == "RESET;") begin
|
||||
o_reset = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_reset = 1'b0;
|
||||
selected = 1'b0;
|
||||
o_sel = 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
$fclose(fd);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
90
sim/tb/tb_svf.v
Normal file
90
sim/tb/tb_svf.v
Normal file
@@ -0,0 +1,90 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_svf();
|
||||
wire [7:0] led_out;
|
||||
wire [31:0] update_count;
|
||||
|
||||
jtag_byte_sink #(
|
||||
.SVF_FILE("sim/other/test.svf"),
|
||||
.TCK_HALF_PERIOD_NS(50)
|
||||
) dut (
|
||||
.o_led(led_out),
|
||||
.o_update_count(update_count)
|
||||
);
|
||||
|
||||
initial begin
|
||||
// Optional waveform dump.
|
||||
$dumpfile("out.vcd");
|
||||
$dumpvars;
|
||||
|
||||
// Timeout for large SVF playback.
|
||||
#20_000;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module jtag_byte_sink #(
|
||||
parameter [8*256-1:0] SVF_FILE = "",
|
||||
parameter integer TCK_HALF_PERIOD_NS = 50
|
||||
)(
|
||||
output reg [7:0] o_led,
|
||||
output reg [31:0] o_update_count
|
||||
);
|
||||
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;
|
||||
wire jtag_tdo;
|
||||
reg [7:0] shift_reg;
|
||||
|
||||
assign jtag_tdo = shift_reg[0];
|
||||
|
||||
jtag_if #(
|
||||
.chain(1),
|
||||
.SVF_FILE(SVF_FILE),
|
||||
.TCK_HALF_PERIOD_NS(TCK_HALF_PERIOD_NS),
|
||||
.USER_IR_OPCODE(32'h0000_0002)
|
||||
) jtag (
|
||||
.i_tdo(jtag_tdo),
|
||||
.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) begin
|
||||
if (jtag_reset) begin
|
||||
shift_reg <= 8'h00;
|
||||
end else if (jtag_sel && jtag_capture) begin
|
||||
shift_reg <= o_led;
|
||||
end else if (jtag_sel && jtag_shift) begin
|
||||
shift_reg <= {jtag_tdi, shift_reg[7:1]};
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge jtag_update or posedge jtag_reset) begin
|
||||
if (jtag_reset) begin
|
||||
o_led <= 8'h00;
|
||||
o_update_count <= 32'd0;
|
||||
end else if (jtag_sel) begin
|
||||
o_led <= shift_reg;
|
||||
o_update_count <= o_update_count + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Keep lint happy for currently unused outputs.
|
||||
wire _unused_tck;
|
||||
wire _unused_runtest;
|
||||
assign _unused_tck = jtag_tck;
|
||||
assign _unused_runtest = jtag_runtest;
|
||||
endmodule
|
||||
@@ -3,7 +3,7 @@ ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
||||
@@ -3,7 +3,7 @@ ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
||||
Reference in New Issue
Block a user