302 lines
6.8 KiB
Verilog
302 lines
6.8 KiB
Verilog
// See LICENSE.SiFive for license details.
|
|
|
|
/*
|
|
* TCP/IP controlled VPI JTAG Interface.
|
|
* Based on Julius Baxter's work on jp_vpi.c
|
|
*
|
|
* Copyright (C) 2012 Franck Jullien, <franck.jullien@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of any
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
//VCS coverage exclude_file
|
|
`define CMD_RESET 0
|
|
`define CMD_TMS_SEQ 1
|
|
`define CMD_SCAN_CHAIN 2
|
|
`define CMD_SCAN_CHAIN_FLIP_TMS 3
|
|
`define CMD_STOP_SIMU 4
|
|
|
|
module JTAGVPI
|
|
#( parameter DEBUG_INFO = 0,
|
|
parameter TP = 1,
|
|
parameter TCK_HALF_PERIOD = 2,// 50, // Clock half period (Clock period = 100 ns => 10 MHz)
|
|
parameter CMD_DELAY = 2, // 1000
|
|
parameter INIT_DELAY = 200
|
|
)
|
|
(
|
|
output jtag_TMS,
|
|
output jtag_TCK,
|
|
output jtag_TDI,
|
|
input jtag_TDO_data,
|
|
input jtag_TDO_driven,
|
|
|
|
input enable,
|
|
input init_done);
|
|
|
|
reg tms;
|
|
reg tck;
|
|
reg tdi;
|
|
wire tdo;
|
|
|
|
assign jtag_TMS = tms;
|
|
assign jtag_TCK = tck;
|
|
assign jtag_TDI = tdi;
|
|
assign tdo = jtag_TDO_driven ? jtag_TDO_data : 1'bz;
|
|
|
|
integer cmd;
|
|
integer length;
|
|
integer nb_bits;
|
|
|
|
reg [31:0] buffer_out [0:4095]; // Data storage from the jtag server
|
|
reg [31:0] buffer_in [0:4095]; // Data storage to the jtag server
|
|
|
|
integer flip_tms;
|
|
|
|
reg [31:0] data_out;
|
|
reg [31:0] data_in;
|
|
|
|
integer debug;
|
|
|
|
initial
|
|
begin
|
|
tck <= #TP 1'b0;
|
|
tdi <= #TP 1'bz;
|
|
tms <= #TP 1'b0;
|
|
|
|
data_out <= 32'h0;
|
|
data_in <= 32'h0;
|
|
|
|
// Small delay to get past reset instability
|
|
// before checking for init_done
|
|
#INIT_DELAY
|
|
|
|
wait(init_done)
|
|
if($test$plusargs("jtag_vpi_enable")) main;
|
|
end
|
|
|
|
task main;
|
|
begin
|
|
$display("JTAG debug module with VPI interface enabled\n");
|
|
|
|
reset_tap;
|
|
goto_run_test_idle_from_reset;
|
|
|
|
while (1) begin
|
|
|
|
// Check for incoming command
|
|
// wait until a command is sent
|
|
// poll with a delay here
|
|
cmd = -1;
|
|
|
|
while (cmd == -1)
|
|
begin
|
|
#CMD_DELAY $check_for_command(cmd, length, nb_bits, buffer_out);
|
|
end
|
|
|
|
// now switch on the command
|
|
case (cmd)
|
|
|
|
`CMD_RESET :
|
|
begin
|
|
if (DEBUG_INFO)
|
|
$display("%t ----> CMD_RESET %h\n", $time, length);
|
|
reset_tap;
|
|
goto_run_test_idle_from_reset;
|
|
end
|
|
|
|
`CMD_TMS_SEQ :
|
|
begin
|
|
if (DEBUG_INFO)
|
|
$display("%t ----> CMD_TMS_SEQ\n", $time);
|
|
do_tms_seq;
|
|
end
|
|
|
|
`CMD_SCAN_CHAIN :
|
|
begin
|
|
if (DEBUG_INFO)
|
|
$display("%t ----> CMD_SCAN_CHAIN\n", $time);
|
|
flip_tms = 0;
|
|
do_scan_chain;
|
|
$send_result_to_server(length, buffer_in);
|
|
end
|
|
|
|
`CMD_SCAN_CHAIN_FLIP_TMS :
|
|
begin
|
|
if(DEBUG_INFO)
|
|
$display("%t ----> CMD_SCAN_CHAIN\n", $time);
|
|
flip_tms = 1;
|
|
do_scan_chain;
|
|
$send_result_to_server(length, buffer_in);
|
|
end
|
|
|
|
`CMD_STOP_SIMU :
|
|
begin
|
|
if(DEBUG_INFO)
|
|
$display("%t ----> End of simulation\n", $time);
|
|
$finish();
|
|
end
|
|
|
|
default:
|
|
begin
|
|
$display("Somehow got to the default case in the command case statement.");
|
|
$display("Command was: %x", cmd);
|
|
$display("Exiting...");
|
|
$finish();
|
|
end
|
|
|
|
endcase // case (cmd)
|
|
|
|
end // while (1)
|
|
end
|
|
|
|
endtask // main
|
|
|
|
|
|
// Generation of the TCK signal
|
|
task gen_clk;
|
|
input [31:0] number;
|
|
integer i;
|
|
|
|
begin
|
|
for (i = 0; i < number; i = i + 1)
|
|
begin
|
|
#TCK_HALF_PERIOD tck <= 1;
|
|
#TCK_HALF_PERIOD tck <= 0;
|
|
end
|
|
end
|
|
|
|
endtask
|
|
|
|
// TAP reset
|
|
task reset_tap;
|
|
begin
|
|
if (DEBUG_INFO)
|
|
$display("(%0t) Task reset_tap", $time);
|
|
tms <= #1 1'b1;
|
|
gen_clk(5);
|
|
end
|
|
|
|
endtask
|
|
|
|
|
|
// Goes to RunTestIdle state
|
|
task goto_run_test_idle_from_reset;
|
|
begin
|
|
if (DEBUG_INFO)
|
|
$display("(%0t) Task goto_run_test_idle_from_reset", $time);
|
|
tms <= #1 1'b0;
|
|
gen_clk(1);
|
|
end
|
|
|
|
endtask
|
|
|
|
//
|
|
task do_tms_seq;
|
|
|
|
integer i,j;
|
|
reg [31:0] data;
|
|
integer nb_bits_rem;
|
|
integer nb_bits_in_this_byte;
|
|
|
|
begin
|
|
if (DEBUG_INFO)
|
|
$display("(%0t) Task do_tms_seq of %d bits (length = %d)", $time, nb_bits, length);
|
|
|
|
// Number of bits to send in the last byte
|
|
nb_bits_rem = nb_bits % 8;
|
|
|
|
for (i = 0; i < length; i = i + 1)
|
|
begin
|
|
// If we are in the last byte, we have to send only
|
|
// nb_bits_rem bits. If not, we send the whole byte.
|
|
nb_bits_in_this_byte = (i == (length - 1)) ? nb_bits_rem : 8;
|
|
|
|
data = buffer_out[i];
|
|
for (j = 0; j < nb_bits_in_this_byte; j = j + 1)
|
|
begin
|
|
tms <= #1 1'b0;
|
|
if (data[j] == 1) begin
|
|
tms <= #1 1'b1;
|
|
end
|
|
gen_clk(1);
|
|
end
|
|
end
|
|
|
|
tms <= #1 1'b0;
|
|
end
|
|
|
|
endtask
|
|
|
|
|
|
//
|
|
task do_scan_chain;
|
|
|
|
integer _bit;
|
|
integer nb_bits_rem;
|
|
integer nb_bits_in_this_byte;
|
|
integer index;
|
|
|
|
begin
|
|
if(DEBUG_INFO)
|
|
$display("(%0t) Task do_scan_chain of %d bits (length = %d)", $time, nb_bits, length);
|
|
|
|
// Number of bits to send in the last byte
|
|
nb_bits_rem = nb_bits % 8;
|
|
|
|
for (index = 0; index < length; index = index + 1)
|
|
begin
|
|
// If we are in the last byte, we have to send only
|
|
// nb_bits_rem bits if it's not zero.
|
|
// If not, we send the whole byte.
|
|
nb_bits_in_this_byte = (index == (length - 1)) ? ((nb_bits_rem == 0) ? 8 : nb_bits_rem) : 8;
|
|
|
|
data_out = buffer_out[index];
|
|
for (_bit = 0; _bit < nb_bits_in_this_byte; _bit = _bit + 1)
|
|
begin
|
|
tdi <= 1'b0;
|
|
if (data_out[_bit] == 1'b1) begin
|
|
tdi <= 1'b1;
|
|
end
|
|
|
|
// On the last bit, set TMS to '1'
|
|
if (((_bit == (nb_bits_in_this_byte - 1)) && (index == (length - 1))) && (flip_tms == 1)) begin
|
|
tms <= 1'b1;
|
|
end
|
|
|
|
#TCK_HALF_PERIOD tck <= 1;
|
|
data_in[_bit] <= tdo;
|
|
#TCK_HALF_PERIOD tck <= 0;
|
|
end
|
|
buffer_in[index] = data_in;
|
|
end
|
|
|
|
tdi <= 1'b0;
|
|
tms <= 1'b0;
|
|
end
|
|
|
|
endtask
|
|
|
|
endmodule
|