From ee52fd4d809ef4c85424742387740e59825d8245 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 10 Sep 2019 17:17:59 +0100 Subject: [PATCH] Add a debug (DMI) bus and a JTAG interface to it on Xilinx FPGAs This adds a simple bus that can be mastered from an external system via JTAG, which will be used to hookup various debug modules. It's loosely based on the RiscV model (hence the DMI name). The module currently only supports hooking up to a Xilinx BSCANE2 but it shouldn't be too hard to adapt it to support different TAPs if necessary. The JTAG protocol proper is not exactly the RiscV one at this point, though I might still change it. This comes with some sim variants of Xilinx BSCANE2 and BUFG and a test bench. Signed-off-by: Benjamin Herrenschmidt --- Makefile | 21 ++- dmi_dtm_dummy.vhdl | 30 ++++ dmi_dtm_tb.vhdl | 214 ++++++++++++++++++++++ dmi_dtm_xilinx.vhdl | 276 +++++++++++++++++++++++++++++ microwatt.core | 16 +- scripts/mw_debug.py | 62 +++++++ sim-unisim/BSCANE2.vhdl | 39 ++++ sim-unisim/BUFG.vhdl | 12 ++ sim-unisim/unisim_vcomponents.vhdl | 45 +++++ soc.vhdl | 31 +++- 10 files changed, 737 insertions(+), 9 deletions(-) create mode 100644 dmi_dtm_dummy.vhdl create mode 100644 dmi_dtm_tb.vhdl create mode 100644 dmi_dtm_xilinx.vhdl create mode 100755 scripts/mw_debug.py create mode 100644 sim-unisim/BSCANE2.vhdl create mode 100644 sim-unisim/BUFG.vhdl create mode 100644 sim-unisim/unisim_vcomponents.vhdl diff --git a/Makefile b/Makefile index 62e9644..a554529 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ GHDL=ghdl -GHDLFLAGS=--std=08 +GHDLFLAGS=--std=08 -Psim-unisim CFLAGS=-O2 -Wall -all = core_tb simple_ram_behavioural_tb soc_reset_tb icache_tb multiply_tb +all = core_tb simple_ram_behavioural_tb soc_reset_tb icache_tb multiply_tb dmi_dtm_tb + # XXX # loadstore_tb fetch_tb @@ -40,10 +41,18 @@ simple_ram_behavioural_helpers.o: simple_ram_behavioural_tb.o: wishbone_types.o simple_ram_behavioural.o simple_ram_behavioural.o: wishbone_types.o simple_ram_behavioural_helpers.o sim_uart.o: wishbone_types.o sim_console.o -soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o simple_ram_behavioural.o +soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o simple_ram_behavioural.o dmi_dtm_xilinx.o wishbone_arbiter.o: wishbone_types.o wishbone_types.o: writeback.o: common.o +dmi_dtm_tb.o: dmi_dtm_xilinx.o +dmi_dtm_xilinx.o: sim-unisim/unisim_vcomponents.o + +UNISIM_BITS = sim-unisim/unisim_vcomponents.vhdl sim-unisim/BSCANE2.vhdl sim-unisim/BUFG.vhdl +sim-unisim/unisim_vcomponents.o: $(UNISIM_BITS) + $(GHDL) -a $(GHDLFLAGS) --work=unisim --workdir=sim-unisim $^ + + fpga/soc_reset_tb.o: fpga/soc_reset.o soc_reset_tb: fpga/soc_reset_tb.o fpga/soc_reset.o @@ -70,6 +79,9 @@ simple_ram_tb: simple_ram_tb.o simple_ram_behavioural_tb: simple_ram_behavioural_helpers_c.o simple_ram_behavioural_tb.o $(GHDL) -e $(GHDLFLAGS) -Wl,simple_ram_behavioural_helpers_c.o $@ +dmi_dtm_tb: dmi_dtm_tb.o + $(GHDL) -e $(GHDLFLAGS) $@ + tests = $(sort $(patsubst tests/%.out,%,$(wildcard tests/*.out))) check: $(tests) test_micropython test_micropython_long @@ -86,4 +98,5 @@ test_micropython_long: core_tb @./scripts/test_micropython_long.py clean: - rm -f *.o work-*cf $(all) + rm -f *.o work-*cf unisim-*cf $(all) + rm -f sim-unisim/*.o sim-unisim/unisim-*cf diff --git a/dmi_dtm_dummy.vhdl b/dmi_dtm_dummy.vhdl new file mode 100644 index 0000000..3cabf38 --- /dev/null +++ b/dmi_dtm_dummy.vhdl @@ -0,0 +1,30 @@ +-- Dummy/empty DMI interface to make toplevel happy on unsupported FPGAs + +library ieee; +use ieee.std_logic_1164.all; + +library work; +use work.wishbone_types.all; + +entity dmi_dtm 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 + ); +end entity dmi_dtm; + +architecture behaviour of dmi_dtm is + dmi_addr <= (others => '0'); + dmi_dout <= (others => '0'); + dmi_req <= '0'; + dmi_wr <= '0'; +end architecture behaviour; + diff --git a/dmi_dtm_tb.vhdl b/dmi_dtm_tb.vhdl new file mode 100644 index 0000000..d872c13 --- /dev/null +++ b/dmi_dtm_tb.vhdl @@ -0,0 +1,214 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.common.all; +use work.wishbone_types.all; + +library unisim; +use unisim.vcomponents.all; + +entity dmi_dtm_tb is +end dmi_dtm_tb; + +architecture behave of dmi_dtm_tb is + signal clk : std_ulogic; + signal rst : std_ulogic; + constant clk_period : time := 10 ns; + constant jclk_period : time := 30 ns; + + -- DMI debug bus signals + signal dmi_addr : std_ulogic_vector(7 downto 0); + signal dmi_din : std_ulogic_vector(63 downto 0); + signal dmi_dout : std_ulogic_vector(63 downto 0); + signal dmi_req : std_ulogic; + signal dmi_wr : std_ulogic; + signal dmi_ack : std_ulogic; + + -- Global JTAG signals (used by BSCANE2 inside dmi_dtm + alias j : glob_jtag_t is glob_jtag; + + -- Wishbone interfaces + signal wishbone_ram_in : wishbone_slave_out; + signal wishbone_ram_out : wishbone_master_out; + +begin + dtm: entity work.dmi_dtm + generic map( + ABITS => 8, + DBITS => 64 + ) + port map( + sys_clk => clk, + sys_reset => rst, + dmi_addr => dmi_addr, + dmi_din => dmi_din, + dmi_dout => dmi_dout, + dmi_req => dmi_req, + dmi_wr => dmi_wr, + dmi_ack => dmi_ack + ); + + -- Dummy loopback until a debug module is present + dmi_din <= dmi_dout; + dmi_ack <= dmi_ack; + + -- system clock + sys_clk: process + begin + clk <= '1'; + wait for clk_period / 2; + clk <= '0'; + wait for clk_period / 2; + end process sys_clk; + + -- system sim: just reset and wait + sys_sim: process + begin + rst <= '1'; + wait for clk_period; + rst <= '0'; + wait; + end process; + + -- jtag sim process + sim_jtag: process + procedure clock(count: in INTEGER) is + begin + for i in 1 to count loop + j.tck <= '0'; + wait for jclk_period/2; + j.tck <= '1'; + wait for jclk_period/2; + end loop; + end procedure clock; + + procedure shift_out(val: in std_ulogic_vector) is + begin + for i in 0 to val'length-1 loop + j.tdi <= val(i); + clock(1); + end loop; + end procedure shift_out; + + procedure shift_in(val: out std_ulogic_vector) is + begin + for i in val'length-1 downto 0 loop + val := j.tdo & val(val'length-1 downto 1); + clock(1); + end loop; + end procedure shift_in; + + procedure send_command( + addr : in std_ulogic_vector(7 downto 0); + data : in std_ulogic_vector(63 downto 0); + op : in std_ulogic_vector(1 downto 0)) is + begin + j.capture <= '1'; + clock(1); + j.capture <= '0'; + clock(1); + j.shift <= '1'; + shift_out(op); + shift_out(data); + shift_out(addr); + j.shift <= '0'; + j.update <= '1'; + clock(1); + j.update <= '0'; + clock(1); + end procedure send_command; + + procedure read_resp( + op : out std_ulogic_vector(1 downto 0); + data : out std_ulogic_vector(63 downto 0)) is + + variable addr : std_ulogic_vector(7 downto 0); + begin + j.capture <= '1'; + clock(1); + j.capture <= '0'; + clock(1); + j.shift <= '1'; + shift_in(op); + shift_in(data); + shift_in(addr); + j.shift <= '0'; + j.update <= '1'; + clock(1); + j.update <= '0'; + clock(1); + end procedure read_resp; + + procedure dmi_write(addr : in std_ulogic_vector(7 downto 0); + data : in std_ulogic_vector(63 downto 0)) is + variable resp_op : std_ulogic_vector(1 downto 0); + variable resp_data : std_ulogic_vector(63 downto 0); + variable timeout : integer; + begin + send_command(addr, data, "10"); + loop + read_resp(resp_op, resp_data); + case resp_op is + when "00" => + return; + when "11" => + timeout := timeout + 1; + assert timeout < 0 + report "dmi_write timed out !" severity error; + when others => + assert 0 > 1 report "dmi_write got odd status: " & + to_hstring(resp_op) severity error; + end case; + end loop; + end procedure dmi_write; + + + procedure dmi_read(addr : in std_ulogic_vector(7 downto 0); + data : out std_ulogic_vector(63 downto 0)) is + variable resp_op : std_ulogic_vector(1 downto 0); + variable timeout : integer; + begin + send_command(addr, (others => '0'), "01"); + loop + read_resp(resp_op, data); + case resp_op is + when "00" => + return; + when "11" => + timeout := timeout + 1; + assert timeout < 0 + report "dmi_read timed out !" severity error; + when others => + assert 0 > 1 report "dmi_read got odd status: " & + to_hstring(resp_op) severity error; + end case; + end loop; + end procedure dmi_read; + + variable data : std_ulogic_vector(63 downto 0); + begin + -- init & reset + j.reset <= '1'; + j.sel <= "0000"; + j.capture <= '0'; + j.update <= '0'; + j.shift <= '0'; + j.tdi <= '0'; + j.tms <= '0'; + j.runtest <= '0'; + clock(5); + j.reset <= '0'; + clock(5); + + -- select chain 2 + j.sel <= "0010"; + clock(1); + + -- send command + dmi_read(x"00", data); + report "Read addr reg:" & to_hstring(data); + std.env.finish; + end process; +end behave; diff --git a/dmi_dtm_xilinx.vhdl b/dmi_dtm_xilinx.vhdl new file mode 100644 index 0000000..bab7ce8 --- /dev/null +++ b/dmi_dtm_xilinx.vhdl @@ -0,0 +1,276 @@ +-- Xilinx internal JTAG to DMI interface +-- +-- DMI bus +-- +-- req : ____/------------\_____ +-- addr: xxxx< >xxxxx +-- 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_reset 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; + +library unisim; +use unisim.vcomponents.all; + +entity dmi_dtm 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 + ); +end entity dmi_dtm; + +architecture behaviour of dmi_dtm is + + -- Signals coming out of the BSCANE2 block + signal jtag_reset : std_ulogic; + signal capture : std_ulogic; + signal update : std_ulogic; + signal drck : std_ulogic; + signal jtag_clk : std_ulogic; + signal sel : std_ulogic; + signal shift : std_ulogic; + signal tdi : std_ulogic; + signal tdo : std_ulogic; + signal tck : 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_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"; + +begin + + -- Implement the Xilinx bscan2 for series 7 devices (TODO: use PoC to + -- wrap this if compatibility is required with older devices). + bscan : BSCANE2 + generic map ( + JTAG_CHAIN => 2 + ) + port map ( + CAPTURE => capture, + DRCK => drck, + RESET => jtag_reset, + RUNTEST => open, + SEL => sel, + SHIFT => shift, + TCK => tck, + TDI => tdi, + TMS => open, + UPDATE => update, + TDO => tdo + ); + + -- Some examples out there suggest buffering the clock so it's + -- treated as a proper clock net. This is probably needed when using + -- drck (the gated clock) but I'm using the real tck here to avoid + -- missing the update phase so maybe not... + -- + clkbuf : BUFG + port map ( +-- I => drck, + I => tck, + O => jtag_clk + ); + + + -- 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) + begin + if jtag_reset = '1' then + shiftr <= (others => '0'); + request <= (others => '0'); + jtag_req <= '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; + diff --git a/microwatt.core b/microwatt.core index b62aef9..04b9d2c 100644 --- a/microwatt.core +++ b/microwatt.core @@ -46,6 +46,14 @@ filesets: - fpga/firmware.hex : {copyto : firmware.hex, file_type : user} file_type : vhdlSource-2008 + debug_xilinx: + files: + - dmi_dtm_xilinx.vhdl : {file_type : vhdlSource-2008} + + debug_dummy: + files: + - dmi_dtm_dummy.vhdl : {file_type : vhdlSource-2008} + nexys_a7: files: - fpga/nexys_a7.xdc : {file_type : xdc} @@ -69,7 +77,7 @@ filesets: targets: nexys_a7: default_tool: vivado - filesets: [core, nexys_a7, soc, fpga] + filesets: [core, nexys_a7, soc, fpga, debug_xilinx] parameters : [memory_size, ram_init_file] tools: vivado: {part : xc7a100tcsg324-1} @@ -77,7 +85,7 @@ targets: nexys_video: default_tool: vivado - filesets: [core, nexys_video, soc, fpga] + filesets: [core, nexys_video, soc, fpga, debug_xilinx] parameters : [memory_size, ram_init_file] tools: vivado: {part : xc7a200tsbg484-1} @@ -85,7 +93,7 @@ targets: arty_a7-35: default_tool: vivado - filesets: [core, arty_a7-35, soc, fpga] + filesets: [core, arty_a7-35, soc, fpga, debug_xilinx] parameters : [memory_size, ram_init_file] tools: vivado: {part : xc7a35ticsg324-1L} @@ -93,7 +101,7 @@ targets: cmod_a7-35: default_tool: vivado - filesets: [core, cmod_a7-35, soc, fpga] + filesets: [core, cmod_a7-35, soc, fpga, debug_xilinx] parameters : [memory_size, ram_init_file, reset_low=false] tools: vivado: {part : xc7a35tcpg236-1} diff --git a/scripts/mw_debug.py b/scripts/mw_debug.py new file mode 100755 index 0000000..fe48743 --- /dev/null +++ b/scripts/mw_debug.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 + +import urjtag; + +def do_command(urc, op, addr, data): + urc.set_dr_in(op,1,0) + urc.set_dr_in(data,65,2) + urc.set_dr_in(addr,73,66) +# print("Sending:", urc.get_dr_in_string()) + urc.shift_dr() + urc.set_dr_in(0x0,73,0) + for x in range(5): + urc.shift_dr() +# print("Received:", urc.get_dr_out_string()) + rsp_code = urc.get_dr_out(1,0) + if rsp_code == 0: + return urc.get_dr_out(65,2) + if rsp_code != 3: + print("Weird response ! rsp=%x" % rsp_code); + print("Timeout sending command !") + +def do_read(urc, addr): + return do_command(urc, 1, addr, 0) + +def do_write(urc, addr, val): + do_command(urc, 2, addr, val) + +def main(): + # Init jtag + #urjtag.loglevel( urjtag.URJ_LOG_LEVEL_ALL ) + + urc = urjtag.chain() + urc.cable("DigilentHS1") + print('Cable frequency:', urc.get_frequency()) + #urc.tap_detect() + #length = urc.len() + #for i in range(0,urc.len()): + # idcode = urc.partid(0) + # print('[%d] 0x%08x' % (i, idcode)) + urc.addpart(6); + print("Part ID: ", urc.partid(0)) + #urc.part(0) + #urc.reset(); + urc.add_register("USER2_REG", 74); + urc.add_instruction("USER2", "000011", "USER2_REG"); + urc.add_register("IDCODE_REG", 32); + urc.add_instruction("IDCODE", "001001", "IDCODE_REG"); + # Send test command + urc.set_instruction("IDCODE") + urc.shift_ir() + urc.shift_dr() + print("Got:", hex(urc.get_dr_out())) + + urc.set_instruction("USER2") + urc.shift_ir() + + print("Reading 0x00: %x" % do_read(urc, 0)) + print("Reading 0xaa: %x" % do_read(urc, 0xaa)) + + +if __name__ == "__main__": + main() diff --git a/sim-unisim/BSCANE2.vhdl b/sim-unisim/BSCANE2.vhdl new file mode 100644 index 0000000..15211fa --- /dev/null +++ b/sim-unisim/BSCANE2.vhdl @@ -0,0 +1,39 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.ALL; + +library unisim; +use unisim.vcomponents.all; + +entity BSCANE2 is + generic(jtag_chain: INTEGER); + port(capture : out std_logic; + drck : out std_logic; + reset : out std_logic; + runtest : out std_logic; + sel : out std_logic; + shift : out std_logic; + tck : out std_logic; + tdi : out std_logic; + tms : out std_logic; + update : out std_logic; + tdo : in std_logic + ); +end BSCANE2; + +architecture behaviour of BSCANE2 is + alias j : glob_jtag_t is glob_jtag; +begin + sel <= j.sel(jtag_chain); + tck <= j.tck; + drck <= tck and sel and (capture or shift); + capture <= j.capture; + reset <= j.reset; + runtest <= j.runtest; + shift <= j.shift; + tdi <= j.tdi; + tms <= j.tms; + update <= j.update; + j.tdo <= tdo; +end architecture behaviour; + diff --git a/sim-unisim/BUFG.vhdl b/sim-unisim/BUFG.vhdl new file mode 100644 index 0000000..462017a --- /dev/null +++ b/sim-unisim/BUFG.vhdl @@ -0,0 +1,12 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +entity BUFG is + port(I : in std_logic; + O : out std_logic + ); +end BUFG; +architecture behaviour of BUFG is +begin + O <= I; +end architecture behaviour; diff --git a/sim-unisim/unisim_vcomponents.vhdl b/sim-unisim/unisim_vcomponents.vhdl new file mode 100644 index 0000000..7faebac --- /dev/null +++ b/sim-unisim/unisim_vcomponents.vhdl @@ -0,0 +1,45 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +package vcomponents is + + -- Global JTAG signals. Xilinx implementation hooks that up to + -- their internal JTAG tap, we just expose them for the testbench + -- to use. These are used by our BSCANE2 block. + -- + type glob_jtag_t is record + reset : std_logic; + tck : std_logic; + tdo : std_logic; + tdi : std_logic; + tms : std_logic; + sel : std_logic_vector(4 downto 1); + capture : std_logic; + shift : std_logic; + update : std_logic; + runtest : std_logic; + end record glob_jtag_t; + signal glob_jtag : glob_jtag_t; + + component BSCANE2 is + generic(jtag_chain: integer); + port(capture : out std_logic; + drck : out std_logic; + reset : out std_logic; + runtest : out std_logic; + sel : out std_logic; + shift : out std_logic; + tck : out std_logic; + tdi : out std_logic; + tms : out std_logic; + update : out std_logic; + tdo : in std_logic + ); + end component BSCANE2; + + component BUFG is + port(I : in std_logic; + O : out std_logic + ); + end component BUFG; +end package vcomponents; diff --git a/soc.vhdl b/soc.vhdl index 4ccbc12..735d86c 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -52,10 +52,18 @@ architecture behaviour of soc is signal wb_bram_out : wishbone_slave_out; constant mem_adr_bits : positive := positive(ceil(log2(real(MEMORY_SIZE)))); - -- Debug signals (used in SIM only) + -- Core debug signals (used in SIM only) signal registers : regfile; signal terminate : std_ulogic; + -- DMI debug bus signals + signal dmi_addr : std_ulogic_vector(7 downto 0); + signal dmi_din : std_ulogic_vector(63 downto 0); + signal dmi_dout : std_ulogic_vector(63 downto 0); + signal dmi_req : std_ulogic; + signal dmi_wr : std_ulogic; + signal dmi_ack : std_ulogic; + begin -- Processor core @@ -177,4 +185,25 @@ begin wishbone_out => wb_bram_out ); + -- 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, + dmi_addr => dmi_addr, + dmi_din => dmi_din, + dmi_dout => dmi_dout, + dmi_req => dmi_req, + dmi_wr => dmi_wr, + dmi_ack => dmi_ack + ); + + -- Dummy loopback until a debug module is present + dmi_din <= dmi_dout; + dmi_ack <= dmi_ack; + end architecture behaviour;