Browse Source
This uses the JTAGG primitive which is similar to BSCANE2. The LUT4 delay approach came from Florian and Greg in https://github.com/enjoy-digital/litex/pull/1087 Has been tested on an OrangeCrab with 48MHz sysclk FT232H up to 30MHz (though libusb/urjtag is by far the bottleneck vs the JTAG clock) Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>pull/346/head

2 changed files with 299 additions and 0 deletions
@ -0,0 +1,298 @@
@@ -0,0 +1,298 @@
|
||||
library ieee; |
||||
use ieee.std_logic_1164.all; |
||||
use ieee.math_real.all; |
||||
|
||||
library work; |
||||
use work.wishbone_types.all; |
||||
|
||||
entity dmi_dtm is |
||||
generic(ABITS : INTEGER:=8; |
||||
DBITS : INTEGER:=64); |
||||
|
||||
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 |
||||
); |
||||
end entity dmi_dtm; |
||||
|
||||
architecture behaviour of dmi_dtm is |
||||
-- Signals coming out of the JTAGG block |
||||
signal jtag_reset_n : std_ulogic; |
||||
signal tdi : std_ulogic; |
||||
signal tdo : std_ulogic; |
||||
signal tck : std_ulogic; |
||||
signal jce1 : std_ulogic; |
||||
signal jshift : std_ulogic; |
||||
signal update : std_ulogic; |
||||
|
||||
-- signals to match dmi_dtb_xilinx |
||||
signal jtag_reset : std_ulogic; |
||||
signal capture : std_ulogic; |
||||
signal jtag_clk : std_ulogic; |
||||
signal sel : std_ulogic; |
||||
signal shift : std_ulogic; |
||||
|
||||
-- delays |
||||
signal jce1_d : std_ulogic; |
||||
constant TCK_DELAY : INTEGER := 8; |
||||
signal tck_d : std_ulogic_vector(TCK_DELAY+1 downto 1); |
||||
|
||||
-- ** 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_clk) |
||||
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"; |
||||
|
||||
-- ECP5 JTAGG |
||||
component JTAGG is |
||||
generic ( |
||||
ER1 : string := "ENABLED"; |
||||
ER2 : string := "ENABLED" |
||||
); |
||||
port( |
||||
JTDO1 : in std_ulogic; |
||||
JTDO2 : in std_ulogic; |
||||
JTDI : out std_ulogic; |
||||
JTCK : out std_ulogic; |
||||
JRTI1 : out std_ulogic; |
||||
JRTI2 : out std_ulogic; |
||||
JSHIFT : out std_ulogic; |
||||
JUPDATE : out std_ulogic; |
||||
JRSTN : out std_ulogic; |
||||
JCE1 : out std_ulogic; |
||||
JCE2 : out std_ulogic |
||||
); |
||||
end component; |
||||
|
||||
component LUT4 is |
||||
generic ( |
||||
INIT : std_logic_vector |
||||
); |
||||
port( |
||||
A : in STD_ULOGIC; |
||||
B : in STD_ULOGIC; |
||||
C : in STD_ULOGIC; |
||||
D : in STD_ULOGIC; |
||||
Z : out STD_ULOGIC |
||||
); |
||||
end component; |
||||
|
||||
begin |
||||
|
||||
jtag: JTAGG |
||||
generic map( |
||||
ER2 => "DISABLED" |
||||
) |
||||
port map ( |
||||
JTDO1 => tdo, |
||||
JTDO2 => '0', |
||||
JTDI => tdi, |
||||
JTCK => tck, |
||||
JRTI1 => open, |
||||
JRTI2 => open, |
||||
JSHIFT => jshift, |
||||
JUPDATE => update, |
||||
JRSTN => jtag_reset_n, |
||||
JCE1 => jce1, |
||||
JCE2 => open |
||||
); |
||||
|
||||
-- JRTI1 looks like it could be connected to SEL, but |
||||
-- in practise JRTI1 is only high briefly, not for the duration |
||||
-- of the transmission. possibly mw_debug could be modified. |
||||
-- The ecp5 is probably the only jtag device anyway. |
||||
sel <= '1'; |
||||
|
||||
-- TDI needs to align with TCK, we use LUT delays here. |
||||
-- From https://github.com/enjoy-digital/litex/pull/1087 |
||||
tck_d(1) <= tck; |
||||
del: for i in 1 to TCK_DELAY generate |
||||
attribute keep : boolean; |
||||
attribute keep of l: label is true; |
||||
begin |
||||
l: LUT4 |
||||
generic map( |
||||
INIT => b"0000_0000_0000_0010" |
||||
) |
||||
port map ( |
||||
A => tck_d(i), |
||||
B => '0', C => '0', D => '0', |
||||
Z => tck_d(i+1) |
||||
); |
||||
end generate; |
||||
jtag_clk <= tck_d(TCK_DELAY+1); |
||||
|
||||
-- capture signal |
||||
jce1_sync : process(jtag_clk) |
||||
begin |
||||
if rising_edge(jtag_clk) then |
||||
jce1_d <= jce1; |
||||
capture <= jce1 and not jce1_d; |
||||
end if; |
||||
end process; |
||||
|
||||
-- latch the shift signal, otherwise |
||||
-- we miss the last shift in |
||||
-- (maybe because we are delaying tck?) |
||||
shift_sync : process(jtag_clk) |
||||
begin |
||||
if (sys_reset = '1') then |
||||
shift <= '0'; |
||||
elsif rising_edge(jtag_clk) then |
||||
shift <= jshift; |
||||
end if; |
||||
end process; |
||||
|
||||
jtag_reset <= not jtag_reset_n; |
||||
|
||||
-- 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_clk, jtag_reset) |
||||
begin |
||||
-- jtag_reset is async (see comments) |
||||
if jtag_reset = '1' then |
||||
dmi_ack_0 <= '0'; |
||||
dmi_ack_1 <= '0'; |
||||
elsif rising_edge(jtag_clk) 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_clk, jtag_reset, sys_reset) |
||||
begin |
||||
if jtag_reset = '1' or sys_reset = '1' then |
||||
shiftr <= (others => '0'); |
||||
jtag_req <= '0'; |
||||
request <= (others => '0'); |
||||
elsif rising_edge(jtag_clk) 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; |
||||
|
Loading…
Reference in new issue