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