From 6544dbe94cccff1d9956d59fefb235cf4c1bd8f7 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 8 Dec 2020 07:21:50 +1100 Subject: [PATCH] First pass at an external JTAG port The verilator simulation interface uses the remote_bitbang protocol from openocd. I have a simple implementation for urjtag too. Signed-off-by: Anton Blanchard --- Makefile | 6 +- dmi_dtm_jtag.vhdl | 302 ++++++++++++++ jtag_tap/tap_top.v | 640 ++++++++++++++++++++++++++++++ soc.vhdl | 65 ++- verilator/jtag-verilator.c | 196 +++++++++ verilator/microwatt-verilator.cpp | 17 + 6 files changed, 1207 insertions(+), 19 deletions(-) create mode 100644 dmi_dtm_jtag.vhdl create mode 100644 jtag_tap/tap_top.v create mode 100644 verilator/jtag-verilator.c diff --git a/Makefile b/Makefile index 2ee5d57..87b69a4 100644 --- a/Makefile +++ b/Makefile @@ -175,7 +175,7 @@ GHDL_IMAGE_GENERICS=-gMEMORY_SIZE=$(MEMORY_SIZE) -gRAM_INIT_FILE=$(RAM_INIT_FILE clkgen=fpga/clk_gen_ecp5.vhd toplevel=fpga/top-generic.vhdl -dmi_dtm=dmi_dtm_dummy.vhdl +dmi_dtm=dmi_dtm_jtag.vhdl dmi_dtm_dummy.vhdl ifeq ($(FPGA_TARGET), verilator) RESET_LOW=true @@ -197,8 +197,8 @@ microwatt.v: $(synth_files) $(RAM_INIT_FILE) $(YOSYS) -m $(GHDLSYNTH) -p "ghdl --std=08 --no-formal $(GHDL_IMAGE_GENERICS) $(GHDL_TARGET_GENERICS) $(synth_files) -e toplevel; write_verilog $@" # Need to investigate why yosys is hitting verilator warnings, and eventually turn on -Wall -microwatt-verilator: microwatt.v verilator/microwatt-verilator.cpp verilator/uart-verilator.c - verilator -O3 -CFLAGS "-DCLK_FREQUENCY=$(CLK_FREQUENCY)" --assert --cc microwatt.v --exe verilator/microwatt-verilator.cpp verilator/uart-verilator.c -o $@ -Iuart16550 -Wno-fatal -Wno-CASEOVERLAP -Wno-UNOPTFLAT #--trace +microwatt-verilator: microwatt.v verilator/microwatt-verilator.cpp verilator/uart-verilator.c verilator/jtag-verilator.c + verilator -O3 -CFLAGS "-DCLK_FREQUENCY=$(CLK_FREQUENCY)" --assert --cc microwatt.v --exe verilator/microwatt-verilator.cpp verilator/uart-verilator.c verilator/jtag-verilator.c -o $@ -Iuart16550 -Ijtag_tap -Wno-fatal -Wno-CASEOVERLAP -Wno-UNOPTFLAT #--trace make -C obj_dir -f Vmicrowatt.mk @cp -f obj_dir/microwatt-verilator microwatt-verilator diff --git a/dmi_dtm_jtag.vhdl b/dmi_dtm_jtag.vhdl new file mode 100644 index 0000000..06f5ce0 --- /dev/null +++ b/dmi_dtm_jtag.vhdl @@ -0,0 +1,302 @@ +-- JTAG to DMI interface, based on the Xilinx version +-- +-- DMI bus +-- +-- req : ____/------------\_____ +-- addr: xxxx< >xxxxx, based on the Xilinx version +-- dout: xxxx< >xxxxx +-- wr : xxxx< >xxxxx +-- din : xxxxxxxxxxxx< >xxx +-- ack : ____________/------\___ +-- +-- * addr/dout set along with req, can be latched on same cycle by slave +-- * ack & din remain up until req is dropped by master, the slave must +-- provide a stable output on din on reads during that time. +-- * req remains low at until at least one sysclk after ack seen down. +-- +-- JTAG (tck) DMI (sys_clk) +-- +-- * jtag_req = 1 +-- (jtag_req_0) * +-- (jtag_req_1) -> * dmi_req = 1 > +-- *.../... +-- * dmi_ack = 1 < +-- * (dmi_ack_0) +-- * <- (dmi_ack_1) +-- * jtag_req = 0 (and latch dmi_din) +-- (jtag_req_0) * +-- (jtag_req_1) -> * dmi_req = 0 > +-- * dmi_ack = 0 < +-- * (dmi_ack_0) +-- * <- (dmi_ack_1) +-- +-- jtag_req can go back to 1 when jtag_rsp_1 is 0 +-- +-- Questions/TODO: +-- - I use 2 flip fops for sync, is that enough ? +-- - I treat the jtag_trst as an async reset, is that necessary ? +-- - Dbl check reset situation since we have two different resets +-- each only resetting part of the logic... +-- - Look at optionally removing the synchronizer on the ack path, +-- assuming JTAG is always slow enough that ack will have been +-- stable long enough by the time CAPTURE comes in. +-- - We could avoid the latched request by not shifting while a +-- request is in progress (and force TDO to 1 to return a busy +-- status). +-- +-- WARNING: This isn't the real DMI JTAG protocol (at least not yet). +-- a command while busy will be ignored. A response of "11" +-- means the previous command is still going, try again. +-- As such We don't implement the DMI "error" status, and +-- we don't implement DTMCS yet... This may still all change +-- but for now it's easier that way as the real DMI protocol +-- requires for a command to work properly that enough TCK +-- are sent while IDLE and I'm having trouble getting that +-- working with UrJtag and the Xilinx BSCAN2 for now. + +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_real.all; + +library work; +use work.wishbone_types.all; + +entity dmi_dtm_jtag is + generic(ABITS : INTEGER:=8; + DBITS : INTEGER:=32); + + port(sys_clk : in std_ulogic; + sys_reset : in std_ulogic; + dmi_addr : out std_ulogic_vector(ABITS - 1 downto 0); + dmi_din : in std_ulogic_vector(DBITS - 1 downto 0); + dmi_dout : out std_ulogic_vector(DBITS - 1 downto 0); + dmi_req : out std_ulogic; + dmi_wr : out std_ulogic; + dmi_ack : in std_ulogic; +-- dmi_err : in std_ulogic TODO: Add error response + jtag_tck : in std_ulogic; + jtag_tdi : in std_ulogic; + jtag_tms : in std_ulogic; + jtag_trst : in std_ulogic; + jtag_tdo : out std_ulogic + ); +end entity dmi_dtm_jtag; + +architecture behaviour of dmi_dtm_jtag is + + -- Signals coming out of the JTAG TAP controller + signal capture : std_ulogic; + signal update : std_ulogic; + signal sel : std_ulogic; + signal shift : std_ulogic; + signal tdi : std_ulogic; + signal tdo : std_ulogic; + + -- ** JTAG clock domain ** + + -- Shift register + signal shiftr : std_ulogic_vector(ABITS + DBITS + 1 downto 0); + + -- Latched request + signal request : std_ulogic_vector(ABITS + DBITS + 1 downto 0); + + -- A request is present + signal jtag_req : std_ulogic; + + -- Synchronizer for jtag_rsp (sys clk -> jtag_tck) + signal dmi_ack_0 : std_ulogic; + signal dmi_ack_1 : std_ulogic; + + -- ** sys clock domain ** + + -- Synchronizer for jtag_req (jtag clk -> sys clk) + signal jtag_req_0 : std_ulogic; + signal jtag_req_1 : std_ulogic; + + -- ** combination signals + signal jtag_bsy : std_ulogic; + signal op_valid : std_ulogic; + signal rsp_op : std_ulogic_vector(1 downto 0); + + -- ** Constants ** + constant DMI_REQ_NOP : std_ulogic_vector(1 downto 0) := "00"; + constant DMI_REQ_RD : std_ulogic_vector(1 downto 0) := "01"; + constant DMI_REQ_WR : std_ulogic_vector(1 downto 0) := "10"; + constant DMI_RSP_OK : std_ulogic_vector(1 downto 0) := "00"; + constant DMI_RSP_BSY : std_ulogic_vector(1 downto 0) := "11"; + + attribute ASYNC_REG : string; + attribute ASYNC_REG of jtag_req_0: signal is "TRUE"; + attribute ASYNC_REG of jtag_req_1: signal is "TRUE"; + attribute ASYNC_REG of dmi_ack_0: signal is "TRUE"; + attribute ASYNC_REG of dmi_ack_1: signal is "TRUE"; + + component tap_top port ( + -- JTAG pads + tms_pad_i : in std_ulogic; + tck_pad_i : in std_ulogic; + trst_pad_i : in std_ulogic; + tdi_pad_i : in std_ulogic; + tdo_pad_o : out std_ulogic; + tdo_padoe_o : out std_ulogic; + + -- TAP states + shift_dr_o : out std_ulogic; + pause_dr_o : out std_ulogic; + update_dr_o : out std_ulogic; + capture_dr_o : out std_ulogic; + + -- Select signals for boundary scan or mbist + extest_select_o : out std_ulogic; + sample_preload_select_o : out std_ulogic; + mbist_select_o : out std_ulogic; + debug_select_o : out std_ulogic; + + -- TDO signal that is connected to TDI of sub-modules. + tdo_o : out std_ulogic; + + -- TDI signals from sub-modules + debug_tdi_i : in std_ulogic; + bs_chain_tdi_i : in std_ulogic; + mbist_tdi_i : in std_ulogic + ); + end component; + +begin + tap_top0 : tap_top + port map ( + tms_pad_i => jtag_tms, + tck_pad_i => jtag_tck, + trst_pad_i => jtag_trst, + tdi_pad_i => jtag_tdi, + tdo_pad_o => jtag_tdo, + tdo_padoe_o => open, -- what to do with this? + + shift_dr_o => shift, + pause_dr_o => open, -- what to do with this? + update_dr_o => update, + capture_dr_o => capture, + + -- connect boundary scan and mbist? + extest_select_o => open, + sample_preload_select_o => open, + mbist_select_o => open, + debug_select_o => sel, + + tdo_o => tdi, + debug_tdi_i => tdo, + bs_chain_tdi_i => '0', + mbist_tdi_i => '0' + ); + + -- dmi_req synchronization + dmi_req_sync : process(sys_clk) + begin + -- sys_reset is synchronous + if rising_edge(sys_clk) then + if (sys_reset = '1') then + jtag_req_0 <= '0'; + jtag_req_1 <= '0'; + else + jtag_req_0 <= jtag_req; + jtag_req_1 <= jtag_req_0; + end if; + end if; + end process; + dmi_req <= jtag_req_1; + + -- dmi_ack synchronization + dmi_ack_sync: process(jtag_tck, jtag_trst) + begin + -- jtag_trst is async (see comments) + if jtag_trst = '1' then + dmi_ack_0 <= '0'; + dmi_ack_1 <= '0'; + elsif rising_edge(jtag_tck) then + dmi_ack_0 <= dmi_ack; + dmi_ack_1 <= dmi_ack_0; + end if; + end process; + + -- jtag_bsy indicates whether we can start a new request, we can when + -- we aren't already processing one (jtag_req) and the synchronized ack + -- of the previous one is 0. + -- + jtag_bsy <= jtag_req or dmi_ack_1; + + -- decode request type in shift register + with shiftr(1 downto 0) select op_valid <= + '1' when DMI_REQ_RD, + '1' when DMI_REQ_WR, + '0' when others; + + -- encode response op + rsp_op <= DMI_RSP_BSY when jtag_bsy = '1' else DMI_RSP_OK; + + -- Some DMI out signals are directly driven from the request register + dmi_addr <= request(ABITS + DBITS + 1 downto DBITS + 2); + dmi_dout <= request(DBITS + 1 downto 2); + dmi_wr <= '1' when request(1 downto 0) = DMI_REQ_WR else '0'; + + -- TDO is wired to shift register bit 0 + tdo <= shiftr(0); + + -- Main state machine. Handles shift registers, request latch and + -- jtag_req latch. Could be split into 3 processes but it's probably + -- not worthwhile. + -- + shifter: process(jtag_tck, jtag_trst, sys_reset) + begin + if jtag_trst = '1' or sys_reset = '1' then + shiftr <= (others => '0'); + jtag_req <= '0'; + request <= (others => '0'); + elsif rising_edge(jtag_tck) then + + -- Handle jtag "commands" when sel is 1 + if sel = '1' then + -- Shift state, rotate the register + if shift = '1' then + shiftr <= tdi & shiftr(ABITS + DBITS + 1 downto 1); + end if; + + -- Update state (trigger) + -- + -- Latch the request if we aren't already processing one and + -- it has a valid command opcode. + -- + if update = '1' and op_valid = '1' then + if jtag_bsy = '0' then + request <= shiftr; + jtag_req <= '1'; + end if; + -- Set the shift register "op" to "busy". This will prevent + -- us from re-starting the command on the next update if + -- the command completes before that. + shiftr(1 downto 0) <= DMI_RSP_BSY; + end if; + + -- Request completion. + -- + -- Capture the response data for reads and clear request flag. + -- + -- Note: We clear req (and thus dmi_req) here which relies on tck + -- ticking and sel set. This means we are stuck with dmi_req up if + -- the jtag interface stops. Slaves must be resilient to this. + -- + if jtag_req = '1' and dmi_ack_1 = '1' then + jtag_req <= '0'; + if request(1 downto 0) = DMI_REQ_RD then + request(DBITS + 1 downto 2) <= dmi_din; + end if; + end if; + + -- Capture state, grab latch content with updated status + if capture = '1' then + shiftr <= request(ABITS + DBITS + 1 downto 2) & rsp_op; + end if; + + end if; + end if; + end process; +end architecture behaviour; diff --git a/jtag_tap/tap_top.v b/jtag_tap/tap_top.v new file mode 100644 index 0000000..e5a74f9 --- /dev/null +++ b/jtag_tap/tap_top.v @@ -0,0 +1,640 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// tap_top.v //// +//// //// +//// //// +//// This file is part of the JTAG Test Access Port (TAP) //// +//// http://www.opencores.org/projects/jtag/ //// +//// //// +//// Author(s): //// +//// Igor Mohor (igorm@opencores.org) //// +//// //// +//// //// +//// All additional information is avaliable in the README.txt //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2000 - 2003 Authors //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// +// +// CVS Revision History +// +// $Log: not supported by cvs2svn $ +// Revision 1.5 2004/01/18 09:27:39 simons +// Blocking non blocking assignmenst fixed. +// +// Revision 1.4 2004/01/17 17:37:44 mohor +// capture_dr_o added to ports. +// +// Revision 1.3 2004/01/14 13:50:56 mohor +// 5 consecutive TMS=1 causes reset of TAP. +// +// Revision 1.2 2004/01/08 10:29:44 mohor +// Control signals for tdo_pad_o mux are changed to negedge. +// +// Revision 1.1 2003/12/23 14:52:14 mohor +// Directory structure changed. New version of TAP. +// +// Revision 1.10 2003/10/23 18:08:01 mohor +// MBIST chain connection fixed. +// +// Revision 1.9 2003/10/23 16:17:02 mohor +// CRC logic changed. +// +// Revision 1.8 2003/10/21 09:48:31 simons +// Mbist support added. +// +// Revision 1.7 2002/11/06 14:30:10 mohor +// Trst active high. Inverted on higher layer. +// +// Revision 1.6 2002/04/22 12:55:56 mohor +// tdo_padoen_o changed to tdo_padoe_o. Signal is active high. +// +// Revision 1.5 2002/03/26 14:23:38 mohor +// Signal tdo_padoe_o changed back to tdo_padoen_o. +// +// Revision 1.4 2002/03/25 13:16:15 mohor +// tdo_padoen_o changed to tdo_padoe_o. Signal was always active high, just +// not named correctly. +// +// Revision 1.3 2002/03/12 14:30:05 mohor +// Few outputs for boundary scan chain added. +// +// Revision 1.2 2002/03/12 10:31:53 mohor +// tap_top and dbg_top modules are put into two separate modules. tap_top +// contains only tap state machine and related logic. dbg_top contains all +// logic necessery for debugging. +// +// Revision 1.1 2002/03/08 15:28:16 mohor +// Structure changed. Hooks for jtag chain added. +// +// +// +// + +// Top module +module tap_top #(parameter + // 0001 version + // 0100100101010001 part number (IQ) + // 00011100001 manufacturer id (flextronics) + // 1 required by standard + IDCODE_VALUE = 32'h149511c3, + IR_LENGTH = 4) + ( + // JTAG pads + tms_pad_i, + tck_pad_i, + trst_pad_i, + tdi_pad_i, + tdo_pad_o, + tdo_padoe_o, + + // TAP states + shift_dr_o, + pause_dr_o, + update_dr_o, + capture_dr_o, + + // Select signals for boundary scan or mbist + extest_select_o, + sample_preload_select_o, + mbist_select_o, + debug_select_o, + + // TDO signal that is connected to TDI of sub-modules. + tdo_o, + + // TDI signals from sub-modules + debug_tdi_i, // from debug module + bs_chain_tdi_i, // from Boundary Scan Chain + mbist_tdi_i // from Mbist Chain + ); + + +// JTAG pins +input tms_pad_i; // JTAG test mode select pad +input tck_pad_i; // JTAG test clock pad +input trst_pad_i; // JTAG test reset pad +input tdi_pad_i; // JTAG test data input pad +output tdo_pad_o; // JTAG test data output pad +output tdo_padoe_o; // Output enable for JTAG test data output pad + +// TAP states +output shift_dr_o; +output pause_dr_o; +output update_dr_o; +output capture_dr_o; + +// Select signals for boundary scan or mbist +output extest_select_o; +output sample_preload_select_o; +output mbist_select_o; +output debug_select_o; + +// TDO signal that is connected to TDI of sub-modules. +output tdo_o; + +// TDI signals from sub-modules +input debug_tdi_i; // from debug module +input bs_chain_tdi_i; // from Boundary Scan Chain +input mbist_tdi_i; // from Mbist Chain + +//Internal constants +localparam EXTEST = 4'b0000; +localparam SAMPLE_PRELOAD = 4'b0001; +localparam IDCODE = 4'b0010; +localparam DEBUG = 4'b1000; +localparam MBIST = 4'b1001; +localparam BYPASS = 4'b1111; + +// Registers +reg test_logic_reset; +reg run_test_idle; +reg select_dr_scan; +reg capture_dr; +reg shift_dr; +reg exit1_dr; +reg pause_dr; +reg exit2_dr; +reg update_dr; +reg select_ir_scan; +reg capture_ir; +reg shift_ir, shift_ir_neg; +reg exit1_ir; +reg pause_ir; +reg exit2_ir; +reg update_ir; +reg extest_select; +reg sample_preload_select; +reg idcode_select; +reg mbist_select; +reg debug_select; +reg bypass_select; +reg tdo_pad_o; +reg tdo_padoe_o; +reg tms_q1, tms_q2, tms_q3, tms_q4; +wire tms_reset; + +assign tdo_o = tdi_pad_i; +assign shift_dr_o = shift_dr; +assign pause_dr_o = pause_dr; +assign update_dr_o = update_dr; +assign capture_dr_o = capture_dr; + +assign extest_select_o = extest_select; +assign sample_preload_select_o = sample_preload_select; +assign mbist_select_o = mbist_select; +assign debug_select_o = debug_select; + + +always @ (posedge tck_pad_i) +begin + tms_q1 <= tms_pad_i; + tms_q2 <= tms_q1; + tms_q3 <= tms_q2; + tms_q4 <= tms_q3; +end + + +assign tms_reset = tms_q1 & tms_q2 & tms_q3 & tms_q4 & tms_pad_i; // 5 consecutive TMS=1 causes reset + + +/********************************************************************************** +* * +* TAP State Machine: Fully JTAG compliant * +* * +**********************************************************************************/ + +// test_logic_reset state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + test_logic_reset<= 1'b1; + else if (tms_reset) + test_logic_reset<= 1'b1; + else + begin + if(tms_pad_i & (test_logic_reset | select_ir_scan)) + test_logic_reset<= 1'b1; + else + test_logic_reset<= 1'b0; + end +end + +// run_test_idle state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + run_test_idle<= 1'b0; + else if (tms_reset) + run_test_idle<= 1'b0; + else + if(~tms_pad_i & (test_logic_reset | run_test_idle | update_dr | update_ir)) + run_test_idle<= 1'b1; + else + run_test_idle<= 1'b0; +end + +// select_dr_scan state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + select_dr_scan<= 1'b0; + else if (tms_reset) + select_dr_scan<= 1'b0; + else + if(tms_pad_i & (run_test_idle | update_dr | update_ir)) + select_dr_scan<= 1'b1; + else + select_dr_scan<= 1'b0; +end + +// capture_dr state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + capture_dr<= 1'b0; + else if (tms_reset) + capture_dr<= 1'b0; + else + if(~tms_pad_i & select_dr_scan) + capture_dr<= 1'b1; + else + capture_dr<= 1'b0; +end + +// shift_dr state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + shift_dr<= 1'b0; + else if (tms_reset) + shift_dr<= 1'b0; + else + if(~tms_pad_i & (capture_dr | shift_dr | exit2_dr)) + shift_dr<= 1'b1; + else + shift_dr<= 1'b0; +end + +// exit1_dr state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + exit1_dr<= 1'b0; + else if (tms_reset) + exit1_dr<= 1'b0; + else + if(tms_pad_i & (capture_dr | shift_dr)) + exit1_dr<= 1'b1; + else + exit1_dr<= 1'b0; +end + +// pause_dr state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + pause_dr<= 1'b0; + else if (tms_reset) + pause_dr<= 1'b0; + else + if(~tms_pad_i & (exit1_dr | pause_dr)) + pause_dr<= 1'b1; + else + pause_dr<= 1'b0; +end + +// exit2_dr state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + exit2_dr<= 1'b0; + else if (tms_reset) + exit2_dr<= 1'b0; + else + if(tms_pad_i & pause_dr) + exit2_dr<= 1'b1; + else + exit2_dr<= 1'b0; +end + +// update_dr state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + update_dr<= 1'b0; + else if (tms_reset) + update_dr<= 1'b0; + else + if(tms_pad_i & (exit1_dr | exit2_dr)) + update_dr<= 1'b1; + else + update_dr<= 1'b0; +end + +// select_ir_scan state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + select_ir_scan<= 1'b0; + else if (tms_reset) + select_ir_scan<= 1'b0; + else + if(tms_pad_i & select_dr_scan) + select_ir_scan<= 1'b1; + else + select_ir_scan<= 1'b0; +end + +// capture_ir state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + capture_ir<= 1'b0; + else if (tms_reset) + capture_ir<= 1'b0; + else + if(~tms_pad_i & select_ir_scan) + capture_ir<= 1'b1; + else + capture_ir<= 1'b0; +end + +// shift_ir state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + shift_ir<= 1'b0; + else if (tms_reset) + shift_ir<= 1'b0; + else + if(~tms_pad_i & (capture_ir | shift_ir | exit2_ir)) + shift_ir<= 1'b1; + else + shift_ir<= 1'b0; +end + +// exit1_ir state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + exit1_ir<= 1'b0; + else if (tms_reset) + exit1_ir<= 1'b0; + else + if(tms_pad_i & (capture_ir | shift_ir)) + exit1_ir<= 1'b1; + else + exit1_ir<= 1'b0; +end + +// pause_ir state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + pause_ir<= 1'b0; + else if (tms_reset) + pause_ir<= 1'b0; + else + if(~tms_pad_i & (exit1_ir | pause_ir)) + pause_ir<= 1'b1; + else + pause_ir<= 1'b0; +end + +// exit2_ir state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + exit2_ir<= 1'b0; + else if (tms_reset) + exit2_ir<= 1'b0; + else + if(tms_pad_i & pause_ir) + exit2_ir<= 1'b1; + else + exit2_ir<= 1'b0; +end + +// update_ir state +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + update_ir<= 1'b0; + else if (tms_reset) + update_ir<= 1'b0; + else + if(tms_pad_i & (exit1_ir | exit2_ir)) + update_ir<= 1'b1; + else + update_ir<= 1'b0; +end + +/********************************************************************************** +* * +* End: TAP State Machine * +* * +**********************************************************************************/ + + + +/********************************************************************************** +* * +* jtag_ir: JTAG Instruction Register * +* * +**********************************************************************************/ +reg [IR_LENGTH-1:0] jtag_ir; // Instruction register +reg [IR_LENGTH-1:0] latched_jtag_ir, latched_jtag_ir_neg; +reg instruction_tdo; + +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + jtag_ir[IR_LENGTH-1:0] <= {IR_LENGTH{1'b0}}; + else if(capture_ir) + jtag_ir <= 4'b0101; // This value is fixed for easier fault detection + else if(shift_ir) + jtag_ir[IR_LENGTH-1:0] <= {tdi_pad_i, jtag_ir[IR_LENGTH-1:1]}; +end + +always @ (negedge tck_pad_i) +begin + instruction_tdo <= jtag_ir[0]; +end +/********************************************************************************** +* * +* End: jtag_ir * +* * +**********************************************************************************/ + + + +/********************************************************************************** +* * +* idcode logic * +* * +**********************************************************************************/ +reg [31:0] idcode_reg; +reg idcode_tdo; + +always @ (posedge tck_pad_i) +begin + if(idcode_select & shift_dr) + idcode_reg <= {tdi_pad_i, idcode_reg[31:1]}; + else + idcode_reg <= IDCODE_VALUE; +end + +always @ (negedge tck_pad_i) +begin + idcode_tdo <= idcode_reg[0]; +end +/********************************************************************************** +* * +* End: idcode logic * +* * +**********************************************************************************/ + + +/********************************************************************************** +* * +* Bypass logic * +* * +**********************************************************************************/ +reg bypassed_tdo; +reg bypass_reg; + +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if (trst_pad_i) + bypass_reg<= 1'b0; + else if(shift_dr) + bypass_reg<= tdi_pad_i; +end + +always @ (negedge tck_pad_i) +begin + bypassed_tdo <= bypass_reg; +end +/********************************************************************************** +* * +* End: Bypass logic * +* * +**********************************************************************************/ + + +/********************************************************************************** +* * +* Activating Instructions * +* * +**********************************************************************************/ +// Updating jtag_ir (Instruction Register) +always @ (posedge tck_pad_i or posedge trst_pad_i) +begin + if(trst_pad_i) + latched_jtag_ir <= IDCODE; // IDCODE selected after reset + else if (tms_reset) + latched_jtag_ir <= IDCODE; // IDCODE selected after reset + else if(update_ir) + latched_jtag_ir <= jtag_ir; +end + +/********************************************************************************** +* * +* End: Activating Instructions * +* * +**********************************************************************************/ + + +// Updating jtag_ir (Instruction Register) +always @ (latched_jtag_ir) +begin + extest_select = 1'b0; + sample_preload_select = 1'b0; + idcode_select = 1'b0; + mbist_select = 1'b0; + debug_select = 1'b0; + bypass_select = 1'b0; + + case(latched_jtag_ir) /* synthesis parallel_case */ + EXTEST: extest_select = 1'b1; // External test + SAMPLE_PRELOAD: sample_preload_select = 1'b1; // Sample preload + IDCODE: idcode_select = 1'b1; // ID Code + MBIST: mbist_select = 1'b1; // Mbist test + DEBUG: debug_select = 1'b1; // Debug + BYPASS: bypass_select = 1'b1; // BYPASS + default: bypass_select = 1'b1; // BYPASS + endcase +end + + + +/********************************************************************************** +* * +* Multiplexing TDO data * +* * +**********************************************************************************/ +always @ (shift_ir_neg or exit1_ir or instruction_tdo or latched_jtag_ir_neg or idcode_tdo or + debug_tdi_i or bs_chain_tdi_i or mbist_tdi_i or + bypassed_tdo) +begin + if(shift_ir_neg) + tdo_pad_o = instruction_tdo; + else + begin + case(latched_jtag_ir_neg) // synthesis parallel_case + IDCODE: tdo_pad_o = idcode_tdo; // Reading ID code + DEBUG: tdo_pad_o = debug_tdi_i; // Debug + SAMPLE_PRELOAD: tdo_pad_o = bs_chain_tdi_i; // Sampling/Preloading + EXTEST: tdo_pad_o = bs_chain_tdi_i; // External test + MBIST: tdo_pad_o = mbist_tdi_i; // Mbist test + default: tdo_pad_o = bypassed_tdo; // BYPASS instruction + endcase + end +end + + +// Tristate control for tdo_pad_o pin +always @ (negedge tck_pad_i) +begin + tdo_padoe_o <= shift_ir | shift_dr | (pause_dr & debug_select); +end +/********************************************************************************** +* * +* End: Multiplexing TDO data * +* * +**********************************************************************************/ + + +always @ (negedge tck_pad_i) +begin + shift_ir_neg <= shift_ir; + latched_jtag_ir_neg <= latched_jtag_ir; +end + + +endmodule diff --git a/soc.vhdl b/soc.vhdl index e4a7895..ee597a9 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -65,7 +65,8 @@ entity soc is LOG_LENGTH : natural := 512; HAS_LITEETH : boolean := false; UART0_IS_16550 : boolean := true; - HAS_UART1 : boolean := false + HAS_UART1 : boolean := false; + HAS_JTAG : boolean := false ); port( rst : in std_ulogic; @@ -100,6 +101,13 @@ entity soc is spi_flash_sdat_oe : out std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0); spi_flash_sdat_i : in std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0) := (others => '1'); + -- JTAG signals + jtag_tck : in std_ulogic := '0'; + jtag_tdi : in std_ulogic := '0'; + jtag_tms : in std_ulogic := '0'; + jtag_trst : in std_ulogic := '0'; + jtag_tdo : out std_ulogic; + -- DRAM controller signals alt_reset : in std_ulogic := '0' ); @@ -840,21 +848,46 @@ begin end generate; -- DMI(debug bus) <-> JTAG bridge - dtm: entity work.dmi_dtm - generic map( - ABITS => 8, - DBITS => 64 - ) - port map( - sys_clk => system_clk, - sys_reset => rst_dtm, - dmi_addr => dmi_addr, - dmi_din => dmi_din, - dmi_dout => dmi_dout, - dmi_req => dmi_req, - dmi_wr => dmi_wr, - dmi_ack => dmi_ack - ); + dmi_jtag: if HAS_JTAG generate + dtm: entity work.dmi_dtm_jtag + generic map( + ABITS => 8, + DBITS => 64 + ) + port map( + sys_clk => system_clk, + sys_reset => rst_dtm, + dmi_addr => dmi_addr, + dmi_din => dmi_din, + dmi_dout => dmi_dout, + dmi_req => dmi_req, + dmi_wr => dmi_wr, + dmi_ack => dmi_ack, + jtag_tck => jtag_tck, + jtag_tdi => jtag_tdi, + jtag_tms => jtag_tms, + jtag_trst => jtag_trst, + jtag_tdo => jtag_tdo + ); + end generate; + + dmi_xilinx: if not HAS_JTAG generate + dtm: entity work.dmi_dtm + generic map( + ABITS => 8, + DBITS => 64 + ) + port map( + sys_clk => system_clk, + sys_reset => rst_dtm, + dmi_addr => dmi_addr, + dmi_din => dmi_din, + dmi_dout => dmi_dout, + dmi_req => dmi_req, + dmi_wr => dmi_wr, + dmi_ack => dmi_ack + ); + end generate; -- DMI interconnect dmi_intercon: process(dmi_addr, dmi_req, diff --git a/verilator/jtag-verilator.c b/verilator/jtag-verilator.c new file mode 100644 index 0000000..e24764a --- /dev/null +++ b/verilator/jtag-verilator.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* XXX Make that some parameter */ +#define TCP_PORT 13245 + +static int fd = -1; +static int cfd = -1; + +static void open_socket(void) +{ + struct sockaddr_in addr; + int opt, rc, flags; + + if (fd >= 0 || fd < -1) + return; + + signal(SIGPIPE, SIG_IGN); + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Failed to open debug socket\r\n"); + goto fail; + } + + rc = 0; + flags = fcntl(fd, F_GETFL); + if (flags >= 0) + rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (flags < 0 || rc < 0) { + fprintf(stderr, "Failed to configure debug socket\r\n"); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(TCP_PORT); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + opt = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (rc < 0) { + fprintf(stderr, "Failed to bind debug socket\r\n"); + goto fail; + } + rc = listen(fd,1); + if (rc < 0) { + fprintf(stderr, "Failed to listen to debug socket\r\n"); + goto fail; + } +#ifdef DEBUG + fprintf(stdout, "Debug socket ready\r\n"); +#endif + return; +fail: + if (fd >= 0) + close(fd); + fd = -2; +} + +static void check_connection(void) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + + cfd = accept(fd, (struct sockaddr *)&addr, &addr_len); + if (cfd < 0) + return; +#ifdef DEBUG + fprintf(stdout, "Debug client connected\r\n"); +#endif +} + +static bool read_one_byte(char *c) +{ + struct pollfd fdset[1]; + int rc; + + if (fd == -1) + open_socket(); + if (fd < 0) + return false; + if (cfd < 0) + check_connection(); + if (cfd < 0) + return false; + + memset(fdset, 0, sizeof(fdset)); + fdset[0].fd = cfd; + fdset[0].events = POLLIN; + rc = poll(fdset, 1, 0); + if (rc <= 0) + return false; + rc = read(cfd, c, 1); + if (rc != 1) { +#ifdef DEBUG + fprintf(stdout, "Debug read error, assuming client disconnected !\r\n"); +#endif + close(cfd); + cfd = -1; + return false; + } + +#ifdef DEBUG + fprintf(stdout, "Got message: %c\n", *c); +#endif + + return true; +} + +static void write_one_byte(char c) +{ + int rc; + +#ifdef DEBUG + fprintf(stdout, "Sending message: %c\r\n", c); +#endif + + rc = write(cfd, &c, 1); + if (rc != 1) { +#ifdef DEBUG + fprintf(stdout, "JTAG write error, disconnecting\r\n"); +#endif + close(cfd); + cfd = -1; + } +} + +struct jtag_in { + uint8_t tck; + uint8_t tms; + uint8_t tdi; + uint8_t trst; +}; + +static struct jtag_in jtag_in; + +struct jtag_in jtag_one_cycle(uint8_t tdo) +{ + char c; + + if (read_one_byte(&c) == false) + goto out; + + // Write request + if ((c >= '0') && (c <= '7')) { + uint8_t val = c - '0'; + + jtag_in.tck = (val >> 2) & 1; + jtag_in.tms = (val >> 1) & 1; + jtag_in.tdi = (val >> 0) & 1; + + goto out; + } + + // Reset request + if ((c >= 'r') && (c <= 'u')) { + uint8_t val = c - 'r'; + + jtag_in.trst = (val >> 1) & 1; + } + + switch (c) { + case 'B': // Blink on + case 'b': // Blink off + goto out; + + case 'R': // Read request + write_one_byte(tdo + '0'); + goto out; + + case 'Q': // Quit request +#ifdef DEBUG + fprintf(stdout, "Disconnecting JTAG\r\n"); +#endif + close(cfd); + cfd = -1; + goto out; + + default: + fprintf(stderr, "Unknown JTAG command %c\r\n", c); + } + +out: + return jtag_in; +} diff --git a/verilator/microwatt-verilator.cpp b/verilator/microwatt-verilator.cpp index 1e82820..db4e5e4 100644 --- a/verilator/microwatt-verilator.cpp +++ b/verilator/microwatt-verilator.cpp @@ -46,6 +46,14 @@ void tick(Vmicrowatt *top) void uart_tx(unsigned char tx); unsigned char uart_rx(void); +struct jtag_in { + unsigned char tck; + unsigned char tms; + unsigned char tdi; + unsigned char trst; +}; +struct jtag_in jtag_one_cycle(uint8_t tdo); + int main(int argc, char **argv) { Verilated::commandArgs(argc, argv); @@ -68,10 +76,19 @@ int main(int argc, char **argv) top->ext_rst = 1; while(!Verilated::gotFinish()) { + struct jtag_in p; + tick(top); uart_tx(top->uart0_txd); top->uart0_rxd = uart_rx(); + + p = jtag_one_cycle(top->jtag_tdo); + + top->jtag_tck = p.tck; + top->jtag_tms = p.tms; + top->jtag_tdi = p.tdi; + top->jtag_trst = p.trst; } #if VM_TRACE