dmi_dtm_ecp5: Use ECP5 JTAGG for DMI
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
parent
eb20195a10
commit
3775650df3
@ -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