forked from cores/microwatt
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
9.7 KiB
VHDL
303 lines
9.7 KiB
VHDL
-- 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;
|