Added JTAG interface with testbench
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user