├── test ├── dp │ ├── .gitignore │ ├── tb │ │ ├── .gitignore │ │ ├── Makefile │ │ └── tb.cpp │ ├── testcase │ │ ├── .gitignore │ │ ├── read_dpidr.cpp │ │ ├── dormant_ignores_access.cpp │ │ ├── targetsel_bad_id.cpp │ │ ├── targetsel_correct_id.cpp │ │ ├── resend_wrong_read.cpp │ │ ├── read_dpidr_idle.cpp │ │ ├── fail_reset_state_bad_register.cpp │ │ ├── read_targetid.cpp │ │ ├── read_dlpidr.cpp │ │ ├── Makefile │ │ ├── targetsel_change_instid.cpp │ │ ├── ap_read_seq.cpp │ │ ├── ap_read_nodelay.cpp │ │ ├── ap_read_delay.cpp │ │ ├── dormant_from_swd.cpp │ │ ├── write_data_parity.cpp │ │ ├── targetsel_bad_parity.cpp │ │ ├── fail_turnround_change.cpp │ │ ├── waves.gtkw │ │ ├── read_parity.cpp │ │ ├── ap_write_seq.cpp │ │ ├── resend_seq_read.cpp │ │ ├── pwrup_req_ack.cpp │ │ ├── ap_read_orundetect.cpp │ │ ├── ap_read_err.cpp │ │ ├── ap_write_err.cpp │ │ ├── ap_readok.cpp │ │ └── ap_write_orundetect.cpp │ └── include │ │ └── tb.h ├── dap │ ├── testcase │ │ ├── .gitignore │ │ ├── read_dpidr.cpp │ │ ├── read_ap_idr.cpp │ │ ├── Makefile │ │ ├── waves.gtkw │ │ ├── apb_read_seq.cpp │ │ ├── apb_write_seq.cpp │ │ └── apb_read_err.cpp │ ├── tb │ │ ├── .gitignore │ │ ├── dap_integration.f │ │ ├── Makefile │ │ ├── dap_integration.v │ │ └── tb.cpp │ └── include │ │ └── tb.h └── common │ ├── include │ └── swd_util.h │ └── swd_util.cpp ├── .gitignore ├── example ├── synth │ ├── Makefile │ ├── .gitignore │ ├── fpga_icebreaker.pcf │ └── Icebreaker.mk ├── fpga │ ├── fpga_icebreaker.f │ └── fpga_icebreaker.v └── picoprobe.cfg ├── doc └── example_system_1.png ├── hdl ├── opendap_mem_ap_apb.f ├── opendap_sw_dp.f ├── cells │ ├── opendap_sync_1bit.v │ └── opendap_swdio_registers.v ├── opendap_swd_dormant_monitor.v ├── opendap_apb_async_bridge.v ├── opendap_mem_ap_apb.v ├── opendap_sw_dp.v └── opendap_sw_dp_serial_comms.v ├── sourceme ├── .gitmodules ├── Readme.md └── LICENSE /test/dp/.gitignore: -------------------------------------------------------------------------------- 1 | *.tmp 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | *.vcd 4 | -------------------------------------------------------------------------------- /test/dp/tb/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | dut.cpp 3 | -------------------------------------------------------------------------------- /example/synth/Makefile: -------------------------------------------------------------------------------- 1 | include Icebreaker.mk 2 | -------------------------------------------------------------------------------- /test/dap/testcase/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | build 3 | -------------------------------------------------------------------------------- /test/dp/testcase/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | build 3 | -------------------------------------------------------------------------------- /test/dap/tb/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | dut.cpp 3 | *.tmp 4 | -------------------------------------------------------------------------------- /doc/example_system_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wren6991/OpenDAP/HEAD/doc/example_system_1.png -------------------------------------------------------------------------------- /test/dap/tb/dap_integration.f: -------------------------------------------------------------------------------- 1 | file dap_integration.v 2 | list $HDL/opendap_sw_dp.f 3 | list $HDL/opendap_mem_ap_apb.f 4 | -------------------------------------------------------------------------------- /hdl/opendap_mem_ap_apb.f: -------------------------------------------------------------------------------- 1 | file opendap_mem_ap_apb.v 2 | file opendap_apb_async_bridge.v 3 | 4 | file cells/opendap_sync_1bit.v 5 | -------------------------------------------------------------------------------- /example/synth/.gitignore: -------------------------------------------------------------------------------- 1 | srcs.mk 2 | *.blif 3 | *.asc 4 | *.hex 5 | *.bin 6 | *.json 7 | *.log 8 | *.config 9 | *.svf 10 | *.bit 11 | -------------------------------------------------------------------------------- /hdl/opendap_sw_dp.f: -------------------------------------------------------------------------------- 1 | file opendap_sw_dp.v 2 | file opendap_sw_dp_serial_comms.v 3 | file opendap_swd_dormant_monitor.v 4 | file cells/opendap_swdio_registers.v 5 | -------------------------------------------------------------------------------- /example/fpga/fpga_icebreaker.f: -------------------------------------------------------------------------------- 1 | file fpga_icebreaker.v 2 | file ../libfpga/common/fpga_reset.v 3 | 4 | list ../../hdl/opendap_sw_dp.f 5 | list ../../hdl/opendap_mem_ap_apb.f 6 | 7 | -------------------------------------------------------------------------------- /sourceme: -------------------------------------------------------------------------------- 1 | export PROJ_ROOT=$PWD 2 | export HDL=$PROJ_ROOT/hdl 3 | export SCRIPTS=$PROJ_ROOT/scripts 4 | 5 | export PATH="$PATH:$PWD/scripts" 6 | export PATH="$PATH:/opt/riscv/bin" 7 | -------------------------------------------------------------------------------- /example/synth/fpga_icebreaker.pcf: -------------------------------------------------------------------------------- 1 | set_io swclk 20 # P2_9, the only GBIN-capable header pin 2 | set_io swdio 21 # P2_3, its vertical neighour on the header 3 | 4 | set_io led 37 # Green on main board 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "scripts"] 2 | path = scripts 3 | url = https://github.com/Wren6991/fpgascripts.git 4 | [submodule "example/libfpga"] 5 | path = example/libfpga 6 | url = https://github.com/Wren6991/libfpga.git 7 | -------------------------------------------------------------------------------- /example/synth/Icebreaker.mk: -------------------------------------------------------------------------------- 1 | CHIPNAME=fpga_icebreaker 2 | DOTF=../fpga/fpga_icebreaker.f 3 | 4 | DEVICE=up5k 5 | PACKAGE=sg48 6 | 7 | include $(SCRIPTS)/synth_ice40.mk 8 | 9 | prog: bit 10 | iceprog $(CHIPNAME).bin 11 | -------------------------------------------------------------------------------- /example/picoprobe.cfg: -------------------------------------------------------------------------------- 1 | adapter driver picoprobe 2 | transport select swd 3 | adapter speed 1000 4 | 5 | source [find target/swj-dp.tcl] 6 | 7 | set _CHIPNAME opendap 8 | set _CPUTAPID 0x0aadf00d 9 | 10 | swj_newdap $_CHIPNAME dp0 -dp-id $_CPUTAPID -instance-id 0 11 | dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dp0 12 | 13 | target create $_CHIPNAME cortex_m -dap $_CHIPNAME.dap 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/dp/testcase/read_dpidr.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: hello world 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | send_dormant_to_swd(t); 9 | swd_line_reset(t); 10 | 11 | uint32_t id; 12 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, id); 13 | tb_assert(status == OK, "Bad status: %d\n", (int)status); 14 | tb_assert(id == DPIDR_EXPECTED, "Bad DPIDR: %08x\n", id); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /test/dp/testcase/dormant_ignores_access.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: make sure the DP ignores line resets in the dormant state 5 | // (and is initially dormant) 6 | 7 | int main() { 8 | tb t("waves.vcd"); 9 | 10 | swd_line_reset(t); 11 | uint32_t id; 12 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, id); 13 | 14 | tb_assert(status == DISCONNECTED, "Should not get any response when in dormant state\n"); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /test/dp/testcase/targetsel_bad_id.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: ensure we get deselected when writing bad TARGETSEL value. 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | send_dormant_to_swd(t); 9 | swd_line_reset(t); 10 | 11 | swd_targetsel(t, (TARGETID_EXPECTED & 0x0fffffffu) | 0x80000000u); 12 | uint32_t id; 13 | swd_status_t status = swd_read(t, DP, 0, id); 14 | tb_assert(status == DISCONNECTED, "Should get no response after bad TARGETSEL\n"); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /test/dp/testcase/targetsel_correct_id.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: ensure we do not get deselected when writing corrected TARGETSEL value. 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | send_dormant_to_swd(t); 9 | swd_line_reset(t); 10 | 11 | swd_targetsel(t, TARGETID_EXPECTED & 0x0fffffffu); 12 | uint32_t id; 13 | swd_status_t status = swd_read(t, DP, 0, id); 14 | tb_assert(status == OK && id == DPIDR_EXPECTED, "Bad DPIDR read after TARGETSEL\n"); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /test/dp/testcase/resend_wrong_read.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check a RESEND after a read that is not READBUF or AP read 5 | // causes a protocol error. 6 | 7 | int main() { 8 | tb t("waves.vcd"); 9 | send_dormant_to_swd(t); 10 | swd_line_reset(t); 11 | 12 | uint32_t id; 13 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, id); 14 | status = swd_read(t, DP, DP_REG_RESEND, id); 15 | tb_assert(status == DISCONNECTED, "Should get immediate protocol error on bad RESEND\n"); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /test/dap/testcase/read_dpidr.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: hello world (make sure the DP is not broken by the DAP 5 | // integration -- canary test for when you have really fucked up) 6 | 7 | int main() { 8 | tb t("waves.vcd"); 9 | send_dormant_to_swd(t); 10 | swd_line_reset(t); 11 | 12 | uint32_t id; 13 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, id); 14 | tb_assert(status == OK, "Bad status: %d\n", (int)status); 15 | tb_assert(id == DPIDR_EXPECTED, "Bad DPIDR: %08x\n", id); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /test/dp/testcase/read_dpidr_idle.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: hello world, make sure the DP actually syncs to the start of the packet. 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | send_dormant_to_swd(t); 9 | idle_clocks(t, 100); 10 | swd_line_reset(t); 11 | idle_clocks(t, 100); 12 | 13 | uint32_t id; 14 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, id); 15 | tb_assert(status == OK, "Bad status: %d\n", (int)status); 16 | tb_assert(id == DPIDR_EXPECTED, "Bad DPIDR: %08x\n", id); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /test/dap/tb/Makefile: -------------------------------------------------------------------------------- 1 | TOP := dap_integration 2 | DOTF := dap_integration.f 3 | SRCS := $(shell listfiles $(DOTF)) 4 | 5 | INCDIR := $(shell yosys-config --datdir)/include ../include ../../common/include 6 | 7 | .PHONY: clean tb all 8 | 9 | all: tb.o 10 | 11 | SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles $(DOTF)); 12 | SYNTH_CMD += write_cxxrtl dut.cpp 13 | 14 | dut.cpp: $(SRCS) 15 | yosys -p "$(SYNTH_CMD)" 2>&1 > cxxrtl.log 16 | 17 | clean:: 18 | rm -f dut.cpp cxxrtl.log tb.o 19 | 20 | tb.o: dut.cpp tb.cpp 21 | clang++ -O3 -std=c++14 -Wall $(addprefix -D,$(CDEFINES)) $(addprefix -I,$(INCDIR)) -c tb.cpp -o tb.o 22 | -------------------------------------------------------------------------------- /test/dp/tb/Makefile: -------------------------------------------------------------------------------- 1 | TOP := opendap_sw_dp 2 | DOTF := ../../../hdl/opendap_sw_dp.f 3 | SRCS := $(shell listfiles $(DOTF)) 4 | 5 | INCDIR := $(shell yosys-config --datdir)/include ../include ../../common/include 6 | 7 | .PHONY: clean tb all 8 | 9 | all: tb.o 10 | 11 | SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles $(DOTF)); 12 | SYNTH_CMD += write_cxxrtl dut.cpp 13 | 14 | dut.cpp: $(SRCS) 15 | yosys -p "$(SYNTH_CMD)" 2>&1 > cxxrtl.log 16 | 17 | clean:: 18 | rm -f dut.cpp cxxrtl.log tb.o 19 | 20 | tb.o: dut.cpp tb.cpp 21 | clang++ -O3 -std=c++14 -Wall $(addprefix -D,$(CDEFINES)) $(addprefix -I,$(INCDIR)) -c tb.cpp -o tb.o 22 | -------------------------------------------------------------------------------- /test/dp/testcase/fail_reset_state_bad_register.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | // Test intent: ensure an invalid access in the reset state causes a protocol error. 4 | 5 | int main() { 6 | tb t("waves.vcd"); 7 | send_dormant_to_swd(t); 8 | swd_line_reset(t); 9 | 10 | // Anything but DPIDR read or TARGETSEL write is a protocol error. 11 | // This is an ABORT. 12 | uint32_t data = 0; 13 | swd_status_t status = swd_write(t, DP, 0, data); 14 | 15 | // DP should immediately drop off the bus, we see three ones due to the parking. 16 | tb_assert(status == DISCONNECTED, "Should get no response to bad access in reset state"); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /test/dap/testcase/read_ap_idr.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check Mem-AP is accessible at APSEL 0 with the correct IDR value 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | 9 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 10 | tb_assert(status == OK, "Failed to connect to DP\n"); 11 | 12 | uint32_t data; 13 | (void)swd_write(t, DP, DP_REG_SELECT, AP_BANK_IDR); 14 | (void)swd_read(t, AP, AP_REG_IDR, data); 15 | status = swd_read(t, DP, DP_REG_RDBUF, data); 16 | 17 | // REVISION = 0, DESIGNER = 7ff, CLASS = Mem-AP, TYPE = APB2/APB3 18 | tb_assert(status == OK && data == APIDR_EXPECTED, "Bad AP IDR read\n"); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/dp/testcase/read_targetid.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | // Test intent: check TARGETID register has expected value. This also 4 | // exercises SELECT.DPBANKSEL writes. 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | send_dormant_to_swd(t); 9 | swd_line_reset(t); 10 | 11 | // DPIDR read to transition to active state 12 | uint32_t data = 0; 13 | (void)swd_read(t, DP, DP_REG_DPIDR, data); 14 | 15 | // Set DPBANKSEL. 16 | data = DP_BANK_TARGETID; 17 | (void)swd_write(t, DP, DP_REG_SELECT, data); 18 | 19 | swd_status_t status = swd_read(t, DP, DP_REG_TARGETID, data); 20 | tb_assert(status == OK, "TARGETID read failed\n"); 21 | tb_assert(data == TARGETID_EXPECTED, "Bad TARGETID: %08x\n", data); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/dp/testcase/read_dlpidr.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | // Test intent: check DLPIDR has the correct value, and responds to changes to instid. 4 | 5 | int main() { 6 | tb t("waves.vcd"); 7 | send_dormant_to_swd(t); 8 | swd_line_reset(t); 9 | 10 | // DPIDR read to transition to active state 11 | uint32_t data = 0; 12 | (void)swd_read(t, DP, DP_REG_DPIDR, data); 13 | 14 | data = DP_BANK_DLPIDR; 15 | (void)swd_write(t, DP, DP_REG_SELECT, data); 16 | (void)swd_read(t, DP, DP_REG_DLPIDR, data); 17 | tb_assert(data == 0x00000001, "Bad DLPIDR initial value\n"); 18 | 19 | t.set_instid(0xf); 20 | (void)swd_read(t, DP, DP_REG_DLPIDR, data); 21 | tb_assert(data == 0xf0000001, "DLPIDR failed to respond to INSTID\n"); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/dap/testcase/Makefile: -------------------------------------------------------------------------------- 1 | TESTCASES := $(wildcard *.cpp) 2 | TEST_EXCECS := $(addprefix build/,$(patsubst %.cpp,%,$(TESTCASES))) 3 | TESTS_RUN := $(addprefix run.,$(patsubst %.cpp,%,$(TESTCASES))) 4 | 5 | INCDIR := $(shell yosys-config --datdir)/include ../include ../../common/include 6 | 7 | .PHONY: all clean 8 | .SECONDARY: 9 | all: $(TESTS_RUN) 10 | 11 | build/%: %.cpp ../tb/tb.o ../../common/swd_util.cpp 12 | mkdir -p build 13 | clang++ -O3 -std=c++14 -Wall $(addprefix -I,$(INCDIR)) $< ../../common/swd_util.cpp ../tb/tb.o -o $@ 14 | 15 | run.%: build/% 16 | ./$< 17 | 18 | # Bit of a hack to trigger tb rebuild when verilog or testbench changes 19 | ../tb/tb.o: ../tb/tb.cpp $(shell listfiles ../tb/dap_integration.f) 20 | make -C ../tb 21 | 22 | clean: 23 | make -C ../tb clean 24 | rm -rf build 25 | -------------------------------------------------------------------------------- /test/dp/testcase/Makefile: -------------------------------------------------------------------------------- 1 | TESTCASES := $(wildcard *.cpp) 2 | TEST_EXCECS := $(addprefix build/,$(patsubst %.cpp,%,$(TESTCASES))) 3 | TESTS_RUN := $(addprefix run.,$(patsubst %.cpp,%,$(TESTCASES))) 4 | 5 | INCDIR := $(shell yosys-config --datdir)/include ../include ../../common/include 6 | 7 | .PHONY: all clean 8 | .SECONDARY: 9 | all: $(TESTS_RUN) 10 | 11 | build/%: %.cpp ../tb/tb.o ../../common/swd_util.cpp 12 | mkdir -p build 13 | clang++ -O3 -std=c++14 -Wall $(addprefix -I,$(INCDIR)) $< ../../common/swd_util.cpp ../tb/tb.o -o $@ 14 | 15 | run.%: build/% 16 | ./$< 17 | 18 | # Bit of a hack to trigger tb rebuild when verilog or testbench changes 19 | ../tb/tb.o: ../tb/tb.cpp $(shell listfiles ../../../hdl/opendap_sw_dp.f) 20 | make -C ../tb 21 | 22 | clean: 23 | make -C ../tb clean 24 | rm -rf build 25 | -------------------------------------------------------------------------------- /test/dp/testcase/targetsel_change_instid.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check the instid signal is checked accurately against the 5 | // TARGETSEL value. 6 | 7 | int main() { 8 | tb t("waves.vcd"); 9 | send_dormant_to_swd(t); 10 | 11 | for (int i = 0; i < 16; ++i) { 12 | t.set_instid(i); 13 | swd_line_reset(t); 14 | swd_targetsel(t, (TARGETID_EXPECTED & 0x0fffffffu) | (uint32_t)i << 28); 15 | uint32_t id; 16 | swd_status_t status = swd_read(t, DP, 0, id); 17 | tb_assert(status == OK && id == DPIDR_EXPECTED, "Failed to select with instid %d\n", i); 18 | } 19 | 20 | swd_line_reset(t); 21 | swd_targetsel(t, TARGETID_EXPECTED & 0x0fffffffu); 22 | uint32_t id; 23 | swd_status_t status = swd_read(t, DP, 0, id); 24 | tb_assert(status == DISCONNECTED, "Failed to fail\n"); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_read_seq.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: perform a sequence of pipelined AP reads, and check that each 5 | // read data response aligns with the correct place in the sequence. 6 | 7 | ap_read_response read_callback(uint16_t addr) { 8 | static uint32_t count = 123; 9 | return { 10 | .rdata = count++, 11 | .delay_cycles = 0, 12 | .err = false 13 | }; 14 | } 15 | 16 | int main() { 17 | tb t("waves.vcd"); 18 | t.set_ap_read_callback(read_callback); 19 | 20 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 21 | tb_assert(status == OK, "Failed to connect to DP\n"); 22 | 23 | // First read primes the pump, its return is not meaningful. 24 | uint32_t data; 25 | (void)swd_read(t, AP, 0, data); 26 | 27 | int n_pipelined_reads = 10; 28 | for (int i = 0; i < n_pipelined_reads; ++i) { 29 | (void)swd_read(t, AP, 0, data); 30 | tb_assert((int)data == 123 + i, "Bad data %d for sequence number %d\n", (int)data, i); 31 | } 32 | (void)swd_read(t, DP, DP_REG_RDBUF, data); 33 | tb_assert(data == 123 + n_pipelined_reads, "Bad data at end.\n"); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /hdl/cells/opendap_sync_1bit.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // A 2FF synchronizer to mitigate metastabilities. Used in the APB async bridge. 7 | 8 | // This is a baseline implementation -- you should replace it with cells 9 | // specific to your FPGA/process 10 | 11 | `ifndef OPENDAP_REG_KEEP_ATTRIBUTE 12 | `define OPENDAP_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *) 13 | `endif 14 | 15 | `default_nettype none 16 | 17 | module opendap_sync_1bit #( 18 | parameter N_STAGES = 2 // Should be >=2 19 | ) ( 20 | input wire clk, 21 | input wire rst_n, 22 | input wire i, 23 | output wire o 24 | ); 25 | 26 | `OPENDAP_REG_KEEP_ATTRIBUTE reg [N_STAGES-1:0] sync_flops; 27 | 28 | always @ (posedge clk or negedge rst_n) 29 | if (!rst_n) 30 | sync_flops <= {N_STAGES{1'b0}}; 31 | else 32 | sync_flops <= {sync_flops[N_STAGES-2:0], i}; 33 | 34 | assign o = sync_flops[N_STAGES-1]; 35 | 36 | endmodule 37 | 38 | `ifndef YOSYS 39 | `default_nettype wire 40 | `endif 41 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_read_nodelay.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check that AP register address and APSEL make it to the AP 5 | // interface, and read data makes it back. 6 | 7 | ap_read_response read_callback(uint16_t addr) { 8 | // Note ADDR is {APSEL, APBANKSEL, A[3:2]}, so 14 bits total. 9 | return { 10 | .rdata = addr, 11 | .delay_cycles = 0, 12 | .err = false 13 | }; 14 | } 15 | 16 | int main() { 17 | tb t("waves.vcd"); 18 | t.set_ap_read_callback(read_callback); 19 | 20 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 21 | tb_assert(status == OK, "Failed to connect to DP\n"); 22 | 23 | for (unsigned apsel = 0; apsel < 256; apsel = apsel ? apsel << 1 : 1) { 24 | for (unsigned bank = 0; bank < 16; ++bank) { 25 | for (unsigned reg = 0; reg < 4; ++reg) { 26 | uint32_t data; 27 | (void)swd_write(t, DP, DP_REG_SELECT, apsel << 24 | bank << 4); 28 | (void)swd_read(t, AP, reg, data); 29 | status = swd_read(t, DP, DP_REG_RDBUF, data); 30 | tb_assert(status == OK && data == (reg | bank << 2 | apsel << 6), 31 | "Bad response, got %08x for APSEL=%u APBANKSEL=%u A[3:2]=%u\n", 32 | data, apsel, bank, reg 33 | ); 34 | } 35 | } 36 | } 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_read_delay.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check that repeated WAIT responses are generated on stalled AP 5 | // read with no ORUNDETECT. 6 | 7 | ap_read_response read_callback(uint16_t addr) { 8 | return { 9 | .rdata = 0xcafef00du, 10 | .delay_cycles = 100, 11 | .err = false 12 | }; 13 | } 14 | 15 | int main() { 16 | tb t("waves.vcd"); 17 | t.set_ap_read_callback(read_callback); 18 | 19 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 20 | tb_assert(status == OK, "Failed to connect to DP\n"); 21 | 22 | uint32_t data; 23 | status = swd_read(t, AP, 0, data); 24 | tb_assert(status == OK, "Priming read should give OK\n"); 25 | status = swd_read(t, DP, DP_REG_RDBUF, data); 26 | tb_assert(status == WAIT, "First stalled AP read attempt should give WAIT\n"); 27 | 28 | // We have ORUNDETECT off, so no sticky errors. Just repeated WAIT. 29 | status = swd_read(t, DP, DP_REG_RDBUF, data); 30 | tb_assert(status == WAIT, "Second stalled AP read attempt should give WAIT\n"); 31 | 32 | idle_clocks(t, 100); 33 | 34 | status = swd_read(t, DP, DP_REG_RDBUF, data); 35 | tb_assert(status == OK, "Should have unblocked by now\n"); 36 | tb_assert(data == 0xcafef00du, "Bad data\n"); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /test/dp/include/tb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "swd_util.h" 10 | 11 | struct ap_read_response { 12 | uint32_t rdata; 13 | int delay_cycles; 14 | bool err; 15 | }; 16 | 17 | struct ap_write_response { 18 | int delay_cycles; 19 | bool err; 20 | }; 21 | 22 | typedef ap_read_response (*ap_read_callback)(uint16_t addr); 23 | 24 | typedef ap_write_response (*ap_write_callback)(uint16_t addr, uint32_t data); 25 | 26 | class tb { 27 | public: 28 | tb(std::string vcdfile); 29 | void set_ap_read_callback(ap_read_callback cb); 30 | void set_ap_write_callback(ap_write_callback cb); 31 | 32 | void set_swclk(bool swclk); 33 | void set_swdi(bool swdi); 34 | bool get_swdo(); 35 | void set_instid(uint8_t instid); 36 | void step(); 37 | private: 38 | int vcd_sample; 39 | bool swclk_prev; 40 | ap_read_callback read_callback; 41 | ap_read_response last_read_response; 42 | ap_write_callback write_callback; 43 | ap_write_response last_write_response; 44 | std::ofstream waves_fd; 45 | cxxrtl::vcd_writer vcd; 46 | cxxrtl::module *dut; 47 | }; 48 | 49 | #define tb_assert(cond, ...) if (!(cond)) {printf(__VA_ARGS__); exit(-1);} 50 | -------------------------------------------------------------------------------- /test/dp/testcase/dormant_from_swd.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: make sure we can return to dormant after going to SWD state. 5 | 6 | const uint8_t swd_to_dormant[9] = { 7 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // at least 50 clocks high (this is 56) 8 | 0xbc, 0xe3 // magic select sequence (starting with 2 clocks low, completing a line reset) 9 | }; 10 | 11 | int main() { 12 | tb t("waves.vcd"); 13 | send_dormant_to_swd(t); 14 | swd_line_reset(t); 15 | 16 | uint32_t id; 17 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, id); 18 | tb_assert(status == OK && id == DPIDR_EXPECTED, "Failed to go to SWD state\n"); 19 | 20 | put_bits(t, swd_to_dormant, 72); 21 | 22 | // The DP should now be unresponsive to line reset. 23 | for (int i = 0; i < 5; ++i) { 24 | swd_line_reset(t); 25 | status = swd_read(t, DP, DP_REG_DPIDR, id); 26 | tb_assert(status == DISCONNECTED, "Failed to return to dormant state\n"); 27 | } 28 | 29 | // We can wake it up again with the magic sequence 30 | send_dormant_to_swd(t); 31 | swd_line_reset(t); 32 | status = swd_read(t, DP, DP_REG_DPIDR, id); 33 | tb_assert(status == OK && id == DPIDR_EXPECTED, "Failed to re-enter SWD state after issuing SWD-to-Dormant\n"); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /test/dap/include/tb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "swd_util.h" 10 | 11 | struct apb_read_response { 12 | uint32_t rdata; 13 | int delay_cycles; 14 | bool err; 15 | }; 16 | 17 | struct apb_write_response { 18 | int delay_cycles; 19 | bool err; 20 | }; 21 | 22 | typedef apb_read_response (*apb_read_callback)(uint32_t addr); 23 | 24 | typedef apb_write_response (*apb_write_callback)(uint32_t addr, uint32_t data); 25 | 26 | class tb { 27 | public: 28 | tb(std::string vcdfile); 29 | void set_apb_read_callback(apb_read_callback cb); 30 | void set_apb_write_callback(apb_write_callback cb); 31 | 32 | void set_swclk(bool swclk); 33 | void set_swdi(bool swdi); 34 | bool get_swdo(); 35 | void set_instid(uint8_t instid); 36 | void step(); 37 | private: 38 | int vcd_sample; 39 | bool swclk_prev; 40 | apb_read_callback read_callback; 41 | apb_read_response last_read_response; 42 | apb_write_callback write_callback; 43 | apb_write_response last_write_response; 44 | std::ofstream waves_fd; 45 | cxxrtl::vcd_writer vcd; 46 | cxxrtl::module *dut; 47 | }; 48 | 49 | #define tb_assert(cond, ...) if (!(cond)) {printf(__VA_ARGS__); exit(-1);} 50 | -------------------------------------------------------------------------------- /test/dp/testcase/write_data_parity.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | // Test intent: 4 | // 5 | // - Check WDATAERR is set by bad write data parity 6 | // 7 | // - Check it can be cleared by an ABORT write 8 | 9 | int main() { 10 | tb t("waves.vcd"); 11 | send_dormant_to_swd(t); 12 | swd_line_reset(t); 13 | 14 | // DPIDR read to transition to active state 15 | uint32_t data = 0; 16 | (void)swd_read(t, DP, 0, data); 17 | 18 | 19 | // Write 0 to ABORT, with a 1 parity bit. (fails even parity check). 20 | uint8_t header = 0x81; 21 | put_bits(t, &header, 8); 22 | hiz_clocks(t, 5); 23 | idle_clocks(t, 32); 24 | put_bits(t, &header, 1); 25 | 26 | // We should not be locked out, but should now get FAULT on e.g. an AP 27 | // write, as a sticky flag is set. 28 | swd_status_t ack = swd_write(t, AP, 0, 0); 29 | 30 | tb_assert(ack == FAULT, "Should get FAULT after write parity error.\n"); 31 | 32 | // Check the correct error flag is set in CTRL/STAT. 33 | uint32_t ctrl_stat = 0; 34 | ack = swd_read(t, DP, 1, ctrl_stat); 35 | tb_assert(ack == OK, "CTRL/STAT read failed\n"); 36 | tb_assert(ctrl_stat & 0x80, "WDATAERR not set.\n"); 37 | 38 | ack = swd_write(t, DP, 0, 0x8); 39 | ack = swd_read(t, DP, 1, ctrl_stat); 40 | tb_assert(ack == OK && !(ctrl_stat & 0x80), "Bad CTRL/STAT after WDATAERR clear\n") 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /test/dap/testcase/waves.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI 3 | [*] Sun Sep 12 11:17:34 2021 4 | [*] 5 | [dumpfile] "/home/luke/proj/opendap/test/dap/testcase/waves.vcd" 6 | [dumpfile_mtime] "Sun Sep 12 11:12:08 2021" 7 | [dumpfile_size] 25032 8 | [savefile] "/home/luke/proj/opendap/test/dap/testcase/waves.gtkw" 9 | [timestart] 0 10 | [size] 3840 2123 11 | [pos] -3891 -938 12 | *-7.000000 492 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 13 | [sst_width] 233 14 | [signals_width] 182 15 | [sst_expanded] 1 16 | [sst_vpaned_height] 674 17 | @200 18 | -Serial 19 | @28 20 | swclk 21 | swdi 22 | swdo 23 | swdo_en 24 | @200 25 | - 26 | --> DP 27 | @28 28 | dp.hostacc_en 29 | dp.hostacc_ap_ndp 30 | dp.hostacc_r_nw 31 | dp.hostacc_addr[1:0] 32 | dp.hostacc_fault 33 | dp.hostacc_wait 34 | @22 35 | dp.hostacc_wdata[31:0] 36 | dp.hostacc_rdata[31:0] 37 | @200 38 | - 39 | --> AP 40 | @22 41 | ap_sel[7:0] 42 | ap.dpacc_addr[5:0] 43 | @28 44 | ap.dpacc_ren 45 | ap.dpacc_wen 46 | ap.dpacc_abort 47 | ap.dpacc_rdy 48 | ap.dpacc_err 49 | @22 50 | ap.dpacc_wdata[31:0] 51 | ap.dpacc_rdata[31:0] 52 | @200 53 | - 54 | --> Downstream APB 55 | @22 56 | dst_paddr[31:0] 57 | @28 58 | dst_pwrite 59 | dst_psel 60 | dst_penable 61 | dst_pready 62 | dst_pslverr 63 | @22 64 | dst_pwdata[31:0] 65 | dst_prdata[31:0] 66 | [pattern_trace] 1 67 | [pattern_trace] 0 68 | -------------------------------------------------------------------------------- /test/dp/testcase/targetsel_bad_parity.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: 5 | 6 | // - Ensure we get deselected when writing the correct TARGETSEL value but 7 | // with a parity error. 8 | // 9 | // - Reconnect and check that WDATAERR was not set. 10 | 11 | int main() { 12 | tb t("waves.vcd"); 13 | send_dormant_to_swd(t); 14 | swd_line_reset(t); 15 | 16 | uint8_t header = 0x99; 17 | put_bits(t, &header, 8); 18 | hiz_clocks(t, 5); 19 | uint8_t targetsel[] = { 20 | TARGETID_EXPECTED & 0xff, 21 | TARGETID_EXPECTED >> 8 & 0xff, 22 | TARGETID_EXPECTED >> 16 & 0xff, 23 | TARGETID_EXPECTED >> 24 & 0x0f 24 | }; 25 | put_bits(t, targetsel, 32); 26 | uint8_t parity = 0; 27 | for (int i = 0; i < 28; ++i) 28 | parity ^= (TARGETID_EXPECTED >> 1) & 0x1; 29 | // Flip the parity bit only. 30 | parity = !parity; 31 | put_bits(t, &parity, 1); 32 | 33 | uint32_t id; 34 | swd_status_t status = swd_read(t, DP, 0, id); 35 | tb_assert(status == DISCONNECTED, "DPIDR read should fail after bad TARGETSEL\n"); 36 | 37 | swd_line_reset(t); 38 | swd_targetsel(t, TARGETID_EXPECTED & 0x0fffffffu); 39 | status = swd_read(t, DP, 0, id); 40 | tb_assert(status == OK && id == DPIDR_EXPECTED, "Couldn't reconnect after bad TARGETSEL\n"); 41 | 42 | uint32_t stat; 43 | status = swd_read(t, DP, 1, stat); 44 | tb_assert(status == OK && !(stat & 0x80), "Didn't see expected STAT value.\n"); 45 | 46 | return 0; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test/dp/testcase/fail_turnround_change.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | // Test intent: make sure invalid DLCR.TURNROUND causes protocol error and 4 | // immediate lockout. 5 | 6 | int main() { 7 | tb t("waves.vcd"); 8 | send_dormant_to_swd(t); 9 | swd_line_reset(t); 10 | uint32_t dpidr = 0; 11 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, dpidr); 12 | 13 | // First check that we can write 0 to DLCR.TURNROUND. 14 | uint32_t dlcr = 0; 15 | status = swd_write(t, DP, DP_REG_SELECT, DP_BANK_DLCR); 16 | status = swd_write(t, DP, DP_REG_DLCR, 0); 17 | status = swd_read(t, DP, DP_REG_DLCR, dlcr); 18 | // Only set bit should be the weird RES1 at position 6. 19 | tb_assert(status == OK && dlcr == 0x40, "Bad DLCR readback\n"); 20 | 21 | status = swd_write(t, DP, DP_REG_DLCR, 0x100); 22 | // The actual failing write should still give an OK, because it's the 23 | // write data that killed us. 24 | tb_assert(status == OK, "Bad DLCR write should still succeed.\n"); 25 | 26 | // Immediately afterward the DP should drop off the bus, because we gave 27 | // an unsupported TURNROUND. 28 | status = swd_read(t, DP, DP_REG_DPIDR, dpidr); 29 | tb_assert(status == DISCONNECTED, "Should be locked out by bad DLCR write.\n"); 30 | 31 | // Line reset should bring it back. 32 | swd_line_reset(t); 33 | status = swd_read(t, DP, DP_REG_DPIDR, dpidr); 34 | tb_assert(status == OK && dpidr == DPIDR_EXPECTED, "Should get good DPIDR readback after reset\n"); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /test/dp/testcase/waves.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI 3 | [*] Thu Sep 9 17:09:17 2021 4 | [*] 5 | [dumpfile] "/home/luke/proj/opendap/test/dp/testcase/waves.vcd" 6 | [dumpfile_mtime] "Thu Sep 9 17:08:39 2021" 7 | [dumpfile_size] 21495 8 | [savefile] "/home/luke/proj/opendap/test/dp/testcase/waves.gtkw" 9 | [timestart] 55 10 | [size] 1920 1043 11 | [pos] -1 -1 12 | *-6.000000 500 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 13 | [sst_width] 233 14 | [signals_width] 190 15 | [sst_expanded] 1 16 | [sst_vpaned_height] 298 17 | @28 18 | swclk 19 | swdi 20 | swdo 21 | swdo_en 22 | @200 23 | - 24 | @28 25 | serial_comms.link_state[1:0] 26 | @22 27 | serial_comms.phase[3:0] 28 | @28 29 | serial_comms.swdi_reg 30 | @22 31 | serial_comms.header_sreg[5:0] 32 | @28 33 | serial_comms.header_sreg_en 34 | @22 35 | serial_comms.data_sreg[31:0] 36 | @28 37 | serial_comms.data_sreg_en 38 | @200 39 | - 40 | @28 41 | hostacc_en 42 | hostacc_r_nw 43 | hostacc_addr[1:0] 44 | hostacc_ap_ndp 45 | hostacc_fault 46 | hostacc_protocol_err 47 | @22 48 | hostacc_rdata[31:0] 49 | hostacc_wdata[31:0] 50 | @200 51 | - 52 | @28 53 | ctrl_stat_stickyerr 54 | ctrl_stat_stickyorun 55 | set_stickyerr 56 | set_stickyorun 57 | serial_comms.dp_set_wdataerr 58 | @200 59 | - 60 | @28 61 | ap_ren 62 | ap_wen 63 | @22 64 | ap_sel[7:0] 65 | ap_addr[5:0] 66 | @28 67 | ap_rdy 68 | ap_err 69 | @22 70 | ap_wdata[31:0] 71 | @23 72 | ap_rdata[31:0] 73 | [pattern_trace] 1 74 | [pattern_trace] 0 75 | -------------------------------------------------------------------------------- /hdl/cells/opendap_swdio_registers.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // IO registers for SWDIO in/out/oen pad signals. All paths between the SWDIO 7 | // pad and the SW-DP pass through these registers 8 | 9 | // This is a baseline implementation -- you should replace it with cells 10 | // specific to your FPGA/process, or replace it with passthrough wires so 11 | // that you can instantiate a registered IO cell at a higher hierarchy 12 | // level. 13 | 14 | `ifndef OPENDAP_REG_KEEP_ATTRIBUTE 15 | `define OPENDAP_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *) 16 | `endif 17 | 18 | `default_nettype none 19 | 20 | module opendap_swdio_flops ( 21 | input wire clk, 22 | input wire rst_n, 23 | 24 | // DP-facing signals 25 | input wire dp_swdo_next, 26 | input wire dp_swdo_en_next, 27 | output wire dp_swdi_prev, 28 | 29 | // Pad-facing signals 30 | output wire pad_swdo, 31 | output wire pad_swdo_en, 32 | input wire pad_swdi 33 | ); 34 | 35 | `OPENDAP_REG_KEEP_ATTRIBUTE reg swdi_reg; 36 | `OPENDAP_REG_KEEP_ATTRIBUTE reg swdo_reg; 37 | `OPENDAP_REG_KEEP_ATTRIBUTE reg swdo_en_reg; 38 | 39 | always @ (posedge clk or negedge rst_n) begin 40 | if (!rst_n) begin 41 | swdi_reg <= 1'b0; 42 | swdo_reg <= 1'b0; 43 | swdo_en_reg <= 1'b0; 44 | end else begin 45 | swdi_reg <= pad_swdi; 46 | swdo_reg <= dp_swdo_next; 47 | swdo_en_reg <= dp_swdo_en_next; 48 | end 49 | end 50 | 51 | assign pad_swdo = swdo_reg; 52 | assign pad_swdo_en = swdo_en_reg; 53 | assign dp_swdi_prev = swdi_reg; 54 | 55 | endmodule 56 | 57 | `ifndef YOSYS 58 | `default_nettype wire 59 | `endif 60 | -------------------------------------------------------------------------------- /test/dp/testcase/read_parity.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check that read parity bit is set correctly for some AP reads 5 | // of random-like data. 6 | 7 | static const int n_data = 20; 8 | static const uint32_t random_data[n_data] = { 9 | 0x91211e6du, 10 | 0x42f8265du, 11 | 0x35bd7f5du, 12 | 0x5d5e63dfu, 13 | 0xa0ff86d8u, 14 | 0x0f11d233u, 15 | 0xd4ced650u, 16 | 0x7d8ae15fu, 17 | 0xf6b22c66u, 18 | 0x078d7694u, 19 | 0x334870b8u, 20 | 0x5abcac27u, 21 | 0xa2d17176u, 22 | 0xe23fa911u, 23 | 0x7e2611d9u, 24 | 0x06a631bbu, 25 | 0x3c4a794cu, 26 | 0x1977ad68u, 27 | 0xaa513352u, 28 | 0xeaec14afu 29 | }; 30 | 31 | ap_read_response read_callback(uint16_t addr) { 32 | static int count = 0; 33 | return { 34 | .rdata = random_data[count++ % n_data], 35 | .delay_cycles = 0, 36 | .err = false 37 | }; 38 | } 39 | 40 | bool even_parity(uint32_t data) { 41 | bool accum = false; 42 | for (int i = 0; i < 32; ++i) 43 | accum ^= (data >> i) & 0x1; 44 | return accum; 45 | } 46 | 47 | // Bits 31:0 of return are data. Bit 32 is parity. 48 | uint64_t swd_read_with_parity(tb &t, uint8_t header) { 49 | put_bits(t, &header, 8); 50 | hiz_clocks(t, 4); 51 | uint8_t rx; 52 | uint64_t accum; 53 | for (int i = 0; i < 33; ++i) { 54 | get_bits(t, &rx, 1); 55 | accum = (accum >> 1) | ((uint64_t)rx << 63); 56 | } 57 | hiz_clocks(t, 1); 58 | return accum >> 31; 59 | } 60 | 61 | int main() { 62 | tb t("waves.vcd"); 63 | t.set_ap_read_callback(read_callback); 64 | 65 | send_dormant_to_swd(t); 66 | swd_line_reset(t); 67 | 68 | uint32_t discard; 69 | (void)swd_read(t, DP, DP_REG_DPIDR, discard); 70 | (void)swd_read(t, AP, 0, discard); 71 | 72 | for (int i = 0; i < n_data; ++i) { 73 | uint64_t data_plus_parity = swd_read_with_parity(t, swd_header(AP, 1, 0)); 74 | bool parity = data_plus_parity >> 32; 75 | uint32_t data = data_plus_parity & 0xffffffffu; 76 | tb_assert(even_parity(data) == parity, "Mismatch for RX data %08x with parity %d\n", data, (int)parity); 77 | } 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_write_seq.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | #include 4 | 5 | // Test intent: check that a sequence of AP writes appear at the AP interface 6 | // in correct order, with correct addresses and correct data. 7 | 8 | std::vector write_history; 9 | 10 | ap_write_response write_callback(uint16_t addr, uint32_t data) { 11 | write_history.push_back((uint64_t)addr << 32 | data); 12 | return { 13 | .delay_cycles = 0, 14 | .err = false 15 | }; 16 | } 17 | 18 | int main() { 19 | tb t("waves.vcd"); 20 | t.set_ap_write_callback(write_callback); 21 | 22 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 23 | tb_assert(status == OK, "Failed to connect to DP\n"); 24 | 25 | const uint32_t magic = 0xabcd1234; 26 | std::vector expected_write_seq; 27 | for (unsigned apsel = 0; apsel < 256; apsel = apsel ? apsel << 1 : 1) { 28 | for (unsigned bank = 0; bank < 16; ++bank) { 29 | status = swd_write(t, DP, DP_REG_SELECT, apsel << 24 | bank << 4); 30 | tb_assert(status == OK, "SELECT failed\n"); 31 | for (unsigned reg = 0; reg < 4; ++reg) { 32 | uint16_t addr_expected = apsel << 6 | bank << 2 | reg; 33 | status = swd_write(t, AP, reg, magic - addr_expected); 34 | expected_write_seq.push_back(magic - addr_expected | (uint64_t)addr_expected << 32); 35 | tb_assert(status == OK, "write failed, APSEL=%u, APBANKSEL=%u, A[3:2]=%u\n", apsel, bank, reg); 36 | } 37 | } 38 | } 39 | idle_clocks(t, 5); 40 | tb_assert( 41 | write_history.size() >= expected_write_seq.size(), 42 | "Not enough write data received, expected %lu, got %lu\n", 43 | expected_write_seq.size(), write_history.size() 44 | ); 45 | tb_assert( 46 | write_history.size() <= expected_write_seq.size(), 47 | "Too much write data received, expected %lu, got %lu\n", 48 | expected_write_seq.size(), write_history.size() 49 | ); 50 | for (size_t i = 0; i < expected_write_seq.size(); ++i){ 51 | tb_assert( 52 | write_history[i] == expected_write_seq[i], 53 | "Bad data item %lu, expected %012lx, got %012lx\n", 54 | i, expected_write_seq[i], write_history[i] 55 | ); 56 | } 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /test/dp/testcase/resend_seq_read.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check that RESEND within a pipelined read sequence returns the 5 | // expected data, and doesn't disturb later reads in the sequence or cause 6 | // spurious AP access. 7 | 8 | ap_read_response read_callback(uint16_t addr) { 9 | static uint32_t count = 0x12340000; 10 | return { 11 | .rdata = count++, 12 | .delay_cycles = 0, 13 | .err = false 14 | }; 15 | } 16 | 17 | int main() { 18 | tb t("waves.vcd"); 19 | t.set_ap_read_callback(read_callback); 20 | 21 | send_dormant_to_swd(t); 22 | swd_line_reset(t); 23 | 24 | uint32_t id; 25 | (void)swd_read(t, DP, DP_REG_DPIDR, id); 26 | 27 | // First read primes the pump, its return is not meaningful. 28 | uint32_t data; 29 | (void)swd_read(t, AP, 0, data); 30 | 31 | int n_pipelined_reads = 10; 32 | for (int i = 0; i < n_pipelined_reads; ++i) { 33 | (void)swd_read(t, AP, 0, data); 34 | tb_assert(data == 0x12340000 + 2 * i, 35 | "Bad first AP read %08x for sequence number %d\n", data, i); 36 | swd_status_t status = swd_read(t, DP, DP_REG_RESEND, data); 37 | tb_assert(status == OK && (int)data == 0x12340000 + 2 * i, 38 | "Bad RESEND for sequence number %d\n", i); 39 | (void)swd_read(t, AP, 0, data); 40 | tb_assert(data == 0x12340000 + 2 * i + 1, 41 | "Bad second AP read %08x for sequence number %d\n", data, i); 42 | } 43 | 44 | (void)swd_read(t, DP, DP_REG_RDBUF, data); 45 | tb_assert(data == 0x12340000 + 2 * n_pipelined_reads, "Bad data from RDBUF.\n"); 46 | swd_status_t status = swd_read(t, DP, DP_REG_RESEND, data); 47 | tb_assert(status == OK && data == 0x12340000 + 2 * n_pipelined_reads, 48 | "Bad RESEND of RDBUF\n"); 49 | status = swd_read(t, DP, DP_REG_RESEND, data); 50 | tb_assert(status == OK && data == 0x12340000 + 2 * n_pipelined_reads, 51 | "Repeated RESEND of same data should succeed\n"); 52 | 53 | status = swd_read(t, DP, DP_REG_DPIDR, data); 54 | tb_assert(status == OK && data == DPIDR_EXPECTED, "Bad DPIDR read after RESEND\n"); 55 | status = swd_read(t, DP, DP_REG_RESEND, data); 56 | tb_assert(status == DISCONNECTED, "RESEND after DPIDR should cause a protocol error\n"); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /test/dap/testcase/apb_read_seq.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: Perform a pipelined sequence of incrementing APB reads. Make 5 | // sure APB gets the correct addresses, and we get the correct data. 6 | 7 | const uint32_t rdata_magic = 0x1234; 8 | const uint32_t start_addr = 0x5a000000; 9 | 10 | apb_read_response read_callback(uint32_t addr) { 11 | return { 12 | .rdata = rdata_magic + addr, 13 | .delay_cycles = 0, 14 | .err = false 15 | }; 16 | } 17 | 18 | int main() { 19 | tb t("waves.vcd"); 20 | t.set_apb_read_callback(read_callback); 21 | 22 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 23 | tb_assert(status == OK, "Failed to connect to DP\n"); 24 | 25 | const uint32_t CSW_ADDR_INC = 0x10u; 26 | (void)swd_write(t, AP, AP_REG_CSW, CSW_ADDR_INC); 27 | (void)swd_write(t, AP, AP_REG_TAR, start_addr); 28 | 29 | uint32_t data; 30 | (void)swd_read(t, AP, AP_REG_TAR, data); 31 | status = swd_read(t, DP, DP_REG_RDBUF, data); 32 | tb_assert(status == OK && data == start_addr, "Bad readback of start address\n"); 33 | 34 | // Priming AP read, kicks off first transfer. Data is not meaningful. 35 | status = swd_read(t, AP, AP_REG_DRW, data); 36 | tb_assert(status == OK, "Should get OK on priming read\n"); 37 | 38 | const int n_pipelined_reads = 10; 39 | for (int i = 0; i < n_pipelined_reads; ++i) { 40 | status = swd_read(t, AP, AP_REG_DRW, data); 41 | tb_assert(status == OK, "Should get OK on all reads with no downstream stalls/errors\n"); 42 | tb_assert(data == rdata_magic + start_addr + i * 4, "Bad data item %i: %08x\n", i, data); 43 | } 44 | status = swd_read(t, AP, AP_REG_DRW, data); 45 | tb_assert(status == OK && data == rdata_magic + start_addr + 4 * n_pipelined_reads, "Bad readback of trailing data\n"); 46 | 47 | // Final address should be +4 for the fact that n+1 reads took place (n of 48 | // them overlapped and 1 not), and +4 again for the fact that TAR always 49 | // points to the *next* transfer address. 50 | (void)swd_read(t, AP, AP_REG_TAR, data); 51 | status = swd_read(t, DP, DP_REG_RDBUF, data); 52 | tb_assert(status == OK && data == start_addr + 4 * (2 + n_pipelined_reads), "Bad ending address\n"); 53 | return 0; 54 | } 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /test/dp/testcase/pwrup_req_ack.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | // Test intent: check CDBGPWRUPREQ/CSYSPWRUPREQ are read/writable, and are 4 | // reflected in their respective ACKs (note we tie REQ -> ACK in the tb) Note 5 | // this test may not run on real hardware due to unrealistic assumptions 6 | // about how quickly the ACKs come back. 7 | 8 | #define MASK_ALL_REQ (DP_CTRL_STAT_CDBGPWRUPREQ | DP_CTRL_STAT_CSYSPWRUPREQ) 9 | #define MASK_ALL_ACK (DP_CTRL_STAT_CDBGPWRUPACK | DP_CTRL_STAT_CSYSPWRUPACK) 10 | #define MASK_ALL_REQ_ACK (MASK_ALL_REQ | MASK_ALL_ACK) 11 | 12 | int main() { 13 | tb t("waves.vcd"); 14 | send_dormant_to_swd(t); 15 | swd_line_reset(t); 16 | 17 | // DPIDR read to transition to active state 18 | uint32_t data = 0; 19 | (void)swd_read(t, DP, DP_REG_DPIDR, data); 20 | 21 | data = DP_BANK_CTRL_STAT; 22 | (void)swd_write(t, DP, DP_REG_SELECT, data); 23 | (void)swd_write(t, DP, DP_REG_CTRL_STAT, 0); 24 | (void)swd_read(t, DP, DP_REG_CTRL_STAT, data); 25 | tb_assert((data & MASK_ALL_REQ) == 0, "Failed to clear REQs\n"); 26 | // Note this may require some delay if run on real hardware: 27 | tb_assert((data & MASK_ALL_ACK) == 0, "ACKs failed to clear\n"); 28 | 29 | (void)swd_write(t, DP, DP_REG_CTRL_STAT, DP_CTRL_STAT_CDBGPWRUPREQ); 30 | (void)swd_read(t, DP, DP_REG_CTRL_STAT, data); 31 | tb_assert(data & DP_CTRL_STAT_CDBGPWRUPREQ, "Failed to set CDBGPWRUPREQ\n"); 32 | tb_assert(data & DP_CTRL_STAT_CDBGPWRUPACK, "No immediate CDBGPWRUPACK\n"); 33 | tb_assert(!(data & DP_CTRL_STAT_CSYSPWRUPACK), "SYSPWRUPACK set unexpectedly\n"); 34 | 35 | (void)swd_write(t, DP, DP_REG_CTRL_STAT, MASK_ALL_REQ); 36 | (void)swd_read(t, DP, DP_REG_CTRL_STAT, data); 37 | // Note setting only SYSPWRREQ is not required to behave predictably 38 | tb_assert((data & MASK_ALL_REQ) == MASK_ALL_REQ, "Failed to set both REQs\n"); 39 | tb_assert((data & MASK_ALL_ACK) == MASK_ALL_ACK, "Failed to immediately get both ACKs\n"); 40 | 41 | (void)swd_write(t, DP, DP_REG_CTRL_STAT, MASK_ALL_REQ | DP_CTRL_STAT_CDBGRSTREQ); 42 | (void)swd_read(t, DP, DP_REG_CTRL_STAT, data); 43 | tb_assert((data & (DP_CTRL_STAT_CDBGRSTREQ | DP_CTRL_STAT_CDBGRSTACK)) == 44 | (DP_CTRL_STAT_CDBGRSTREQ | DP_CTRL_STAT_CDBGRSTACK), "Failed to set RSTREQ/ACK"); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_read_orundetect.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check that WAIT->FAULT sequence with skipped data phases is 5 | // generated on stalled AP read with ORUNDETECT set. 6 | 7 | ap_read_response read_callback(uint16_t addr) { 8 | static uint32_t count; 9 | return { 10 | .rdata = 0xcafef00du + count, 11 | .delay_cycles = 500, 12 | .err = false 13 | }; 14 | } 15 | 16 | const uint32_t MASK_ORUNDETECT = 0x1; 17 | const uint32_t MASK_STICKYORUN = 0x2; 18 | const uint32_t MASK_ORUNERRCLR = 0x10; 19 | 20 | int main() { 21 | tb t("waves.vcd"); 22 | t.set_ap_read_callback(read_callback); 23 | 24 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 25 | tb_assert(status == OK, "Failed to connect to DP\n"); 26 | 27 | (void)swd_write(t, DP, DP_REG_CTRL_STAT, MASK_ORUNDETECT); 28 | uint32_t data; 29 | (void)swd_read(t, DP, DP_REG_CTRL_STAT, data); 30 | tb_assert(data & MASK_ORUNDETECT, "Failed to set CTRL/STAT.ORUNDETECT.\n"); 31 | 32 | 33 | status = swd_read_orun(t, AP, 0, data); 34 | tb_assert(status == OK, "Priming read should give OK\n"); 35 | 36 | // Because our host code is ignoring response and always driving 33 hi-Z 37 | // clocks on read, and we have a pullup on the bus, the DP will see a 38 | // protocol error if it is not also skipping through the address phase. 39 | // At that point we would get DISCONNECTED responses instead of WAIT/FAULT. 40 | status = swd_read_orun(t, AP, 0, data); 41 | tb_assert(status == WAIT, "First read on stall should WAIT\n"); 42 | status = swd_read_orun(t, AP, 0, data); 43 | tb_assert(status == FAULT, "Second read on stall should FAULT\n"); 44 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 45 | tb_assert(status == OK, "CTRL/STAT read during stall + overrun should still give OK\n"); 46 | 47 | idle_clocks(t, 500); 48 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 49 | tb_assert(status == OK, "Should be able to read CTRL/STAT after stall clears\n"); 50 | tb_assert(data & MASK_STICKYORUN, "STICKYORUN should be set.\n"); 51 | 52 | swd_write_orun(t, DP, DP_REG_ABORT, MASK_ORUNERRCLR); 53 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 54 | tb_assert(status == OK, "Should be able to read CTRL/STAT after ABORT\n"); 55 | tb_assert(!(data & MASK_STICKYORUN), "STICKYORUN should be cleared by ABORT.\n"); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /test/dap/testcase/apb_write_seq.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: Check an APB read error correctly sets STICKYERR, and we can 5 | // then recover from the error and issue more transfers. 6 | 7 | const uint32_t wdata_magic = 0x00c30000; 8 | const uint32_t start_addr = 0x5a000000; 9 | 10 | std::vector write_history; 11 | 12 | apb_write_response write_callback(uint32_t addr, uint32_t data) { 13 | write_history.push_back((uint64_t)addr << 32 | data); 14 | return { 15 | .delay_cycles = 0, 16 | .err = false 17 | }; 18 | } 19 | 20 | int main() { 21 | tb t("waves.vcd"); 22 | t.set_apb_write_callback(write_callback); 23 | 24 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 25 | tb_assert(status == OK, "Failed to connect to DP\n"); 26 | 27 | const uint32_t CSW_ADDR_INC = 0x10u; 28 | (void)swd_write(t, AP, AP_REG_CSW, CSW_ADDR_INC); 29 | (void)swd_write(t, AP, AP_REG_TAR, start_addr); 30 | 31 | std::vector expected_write_seq; 32 | 33 | const int n_writes = 10; 34 | for (int i = 0; i < n_writes; ++i) { 35 | status = swd_write(t, AP, AP_REG_DRW, wdata_magic + i); 36 | tb_assert(status ==OK, "Should get OK for non-waited non-errored write sequence.\n"); 37 | // Need some space for the async bridge, because write strobe at end 38 | // of data phase is quite close to next response phase. Reads OTOH 39 | // are fine. 40 | idle_clocks(t, 10); 41 | expected_write_seq.push_back(wdata_magic + i | ((uint64_t)(start_addr + 4 * i) << 32)); 42 | } 43 | 44 | idle_clocks(t, 50); 45 | tb_assert( 46 | write_history.size() >= expected_write_seq.size(), 47 | "Not enough write data received, expected %lu, got %lu\n", 48 | expected_write_seq.size(), write_history.size() 49 | ); 50 | tb_assert( 51 | write_history.size() <= expected_write_seq.size(), 52 | "Too much write data received, expected %lu, got %lu\n", 53 | expected_write_seq.size(), write_history.size() 54 | ); 55 | for (size_t i = 0; i < expected_write_seq.size(); ++i){ 56 | tb_assert( 57 | write_history[i] == expected_write_seq[i], 58 | "Bad data item %lu, expected %012lx, got %012lx\n", 59 | i, expected_write_seq[i], write_history[i] 60 | ); 61 | } 62 | 63 | uint32_t data; 64 | (void)swd_read(t, AP, AP_REG_TAR, data); 65 | status = swd_read(t, DP, DP_REG_RDBUF, data); 66 | // TAR points to the *next* transfer address. 67 | tb_assert(status == OK && data == start_addr + 4 * n_writes, "Bad final TAR value %08x\n", data); 68 | 69 | return 0; 70 | } 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_read_err.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: Check that an AP read error sets STICKYERR and generates a 5 | // FAULT response, and that STICKYERR can then be cleared, and the AP can be 6 | // re-accessed succesfully. 7 | 8 | ap_read_response read_callback(uint16_t addr) { 9 | static uint32_t count = 0; 10 | if (count == 0) { 11 | return { 12 | .rdata = count++ + 123, 13 | .delay_cycles = 100, 14 | .err = true 15 | }; 16 | } 17 | else { 18 | return { 19 | .rdata = count++ + 123, 20 | .delay_cycles = 0, 21 | .err = false 22 | }; 23 | } 24 | } 25 | 26 | int main() { 27 | tb t("waves.vcd"); 28 | t.set_ap_read_callback(read_callback); 29 | 30 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 31 | tb_assert(status == OK, "Failed to connect to DP\n"); 32 | 33 | uint32_t data; 34 | status = swd_read(t, AP, 0, data); 35 | if (status != OK) { 36 | printf("Priming read should give OK\n"); 37 | return -1; 38 | } 39 | for (int i = 0; i < 2; ++i) { 40 | status = swd_read(t, AP, 0, data); 41 | if (status != WAIT) { 42 | printf("AP access should be stalled.\n"); 43 | return 1; 44 | } 45 | } 46 | 47 | idle_clocks(t, 100); 48 | status = swd_read(t, AP, 0, data); 49 | tb_assert(status == FAULT, "Failed AP access should give FAULT.\n"); 50 | 51 | status = swd_write(t, DP, DP_REG_SELECT, DP_BANK_CTRL_STAT); 52 | // Bit of a weird corner of the spec here: you can't write SELECT when 53 | // there is a sticky flag set, so it's impossible to actually read 54 | // CTRL/STAT if your DPBANKSEL is not already 0. Luckily in this test it 55 | // is already 0, and this is usually true in practice too. 56 | tb_assert(status == FAULT, "Should get FAULT on select too\n"); 57 | 58 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 59 | tb_assert(status == OK, "Should never get FAULT on CTRL/STAT read\n"); 60 | tb_assert(data & 0x20, "STICKYERR should be set.\n"); 61 | tb_assert(!(data & 0x92), "No other sticky flags should be set.\n"); 62 | 63 | (void)swd_write(t, DP, DP_REG_ABORT, 0x4); 64 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 65 | tb_assert(status == OK && !(data & 0x20), "STICKYERR should be cleared by ABORT\n"); 66 | 67 | (void)swd_read(t, AP, 0, data); 68 | status = swd_read(t, DP, DP_REG_RDBUF, data); 69 | 70 | // The reads return a count sequence from 123 (including the first failing 71 | // write). +1 confirms the AP didn't see any reads between the faulting 72 | // one and this one. 73 | tb_assert(status == OK && data == 123 + 1, "Bad final readback\n"); 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /test/dap/testcase/apb_read_err.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: Check an APB read error correctly sets STICKYERR, and we can 5 | // then recover from the error and issue more transfers. 6 | 7 | const uint32_t rdata_magic = 0x1234; 8 | const uint32_t start_addr = 0x5a000000; 9 | 10 | apb_read_response read_callback(uint32_t addr) { 11 | static int count = 0; 12 | return { 13 | .rdata = rdata_magic + addr, 14 | .delay_cycles = 0, 15 | .err = count++ == 0 16 | }; 17 | } 18 | 19 | int main() { 20 | tb t("waves.vcd"); 21 | t.set_apb_read_callback(read_callback); 22 | 23 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 24 | tb_assert(status == OK, "Failed to connect to DP\n"); 25 | 26 | const uint32_t CSW_ADDR_INC = 0x10u; 27 | (void)swd_write(t, AP, AP_REG_CSW, CSW_ADDR_INC); 28 | (void)swd_write(t, AP, AP_REG_TAR, start_addr); 29 | 30 | // Priming AP read, kicks off first transfer. Data is not meaningful. 31 | uint32_t data; 32 | status = swd_read(t, AP, AP_REG_DRW, data); 33 | tb_assert(status == OK, "Should get OK on priming read\n"); 34 | 35 | // Note this is also an AP read, not RDBUFF. Normally this would cause a 36 | // TAR increment, but in this case it doesn't because the DP squashes the 37 | // access with a FAULT response. We'll check the address. 38 | status = swd_read(t, AP, AP_REG_DRW, data); 39 | tb_assert(status == FAULT, "Should get FAULT on retiring read\n"); 40 | 41 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 42 | tb_assert(status == OK, "Should never get FAULT on CTRL/STAT read\n"); 43 | tb_assert(data & 0x20, "STICKYERR should be set.\n"); 44 | tb_assert(!(data & 0x92), "No other sticky flags should be set.\n"); 45 | 46 | (void)swd_write(t, DP, DP_REG_ABORT, 0x4); 47 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 48 | tb_assert(status == OK && !(data & 0x20), "STICKYERR should be cleared by ABORT\n"); 49 | 50 | // All better, we can read again. 51 | status = swd_read(t, AP, AP_REG_DRW, data); 52 | tb_assert(status == OK, "Should get OK on read after ABORT\n"); 53 | status = swd_read(t, DP, DP_REG_RDBUF, data); 54 | // Only one prior transfer that wasn't squashed by the DP, so we are 55 | // reading through a once-incremented TAR. 56 | tb_assert(status == OK && data == start_addr + rdata_magic + 4, "Bad readback %08x\n", data); 57 | (void)swd_read(t, AP, AP_REG_TAR, data); 58 | status = swd_read(t, DP, DP_REG_RDBUF, data); 59 | // TAR points to the *next* transfer address. 60 | tb_assert(status == OK && data == start_addr + 8, "Bad final TAR value %08x\n", data); 61 | 62 | return 0; 63 | } 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_write_err.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | #include 4 | 5 | // Test intent: check that an AP write error sets the STICKYERR flag, and we 6 | // can clear the flag and perform another AP write. 7 | 8 | std::vector write_history; 9 | 10 | ap_write_response write_callback(uint16_t addr, uint32_t data) { 11 | write_history.push_back((uint64_t)addr << 32 | data); 12 | // Error response on the first transfer only. 13 | static int ctr = 0; 14 | return { 15 | .delay_cycles = 0, 16 | .err = ctr++ == 0 17 | }; 18 | } 19 | 20 | static const uint32_t MASK_STICKYERR = 0x20; 21 | static const uint32_t MASK_STKERRCLR = 0x04; 22 | 23 | int main() { 24 | tb t("waves.vcd"); 25 | t.set_ap_write_callback(write_callback); 26 | 27 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 28 | tb_assert(status == OK, "Failed to connect to DP\n"); 29 | 30 | const uint32_t magic = 0xabcd1234; 31 | std::vector expected_write_seq; 32 | uint32_t ctr = 0; 33 | expected_write_seq.push_back(magic + ctr); 34 | status = swd_write(t, AP, 0, magic + ctr++); 35 | tb_assert(status == OK, "Erroring write should itself give OK\n"); 36 | status = swd_write(t, AP, 0, magic + ctr++); 37 | tb_assert(status == FAULT, "Next access should immediately fault.\n"); 38 | 39 | uint32_t data; 40 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 41 | tb_assert(status == OK, "DP read should not be affected by sticky flags.\n"); 42 | tb_assert(data & MASK_STICKYERR, "STICKYERR should be set.\n"); 43 | (void)swd_write(t, DP, DP_REG_ABORT, MASK_STKERRCLR); 44 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 45 | tb_assert(status == OK && !(data & MASK_STICKYERR), "Failed to clear STICKYERR\n"); 46 | 47 | for (int i = 0; i < 5; ++i) { 48 | expected_write_seq.push_back(magic + ctr); 49 | status = swd_write(t, AP, 0, magic + ctr++); 50 | tb_assert(status == OK, "Should be able to write after clearing STICKYERR\n"); 51 | } 52 | 53 | idle_clocks(t, 5); 54 | tb_assert( 55 | write_history.size() >= expected_write_seq.size(), 56 | "Not enough write data received, expected %lu, got %lu\n", 57 | expected_write_seq.size(), write_history.size() 58 | ); 59 | tb_assert( 60 | write_history.size() <= expected_write_seq.size(), 61 | "Too much write data received, expected %lu, got %lu\n", 62 | expected_write_seq.size(), write_history.size() 63 | ); 64 | for (size_t i = 0; i < expected_write_seq.size(); ++i){ 65 | tb_assert( 66 | write_history[i] == expected_write_seq[i], 67 | "Bad data item %lu, expected %012lx, got %012lx\n", 68 | i, expected_write_seq[i], write_history[i] 69 | ); 70 | } 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | OpenDAP 2 | ======= 3 | 4 | *OpenDAP is an early work-in-progress.* 5 | 6 | OpenDAP is an implementation of a Debug Access Port (DAP), based on the ADIv5.2 specification, issue F. More specifically, it implements the following Debug Port (DP) and Access Port (AP) components: 7 | 8 | - An SW-DP 9 | - Provides connection from an external Serial Wire Debug probe to one or more downstream APs 10 | - Implements a DPv2 with the MINDP extension (no transaction counter or pushed compare/verify) 11 | - Implements SWDv2 protocol, with multidrop support 12 | - A Mem-AP 13 | - Provides further connection to downstream memory-mapped devices 14 | - Downstream interface is AMBA 3 APB 15 | 16 |

A block diagram. At the top is a DP, with an SWD connection to the outside world. Below this, connected via a stripped-down APB interface, is a Mem-AP. This is connected with APB to a Debug Module box, which is then connected using some unspecified interface to a pair of RISC-V cores.

17 | 18 | In the depicted example system, OpenDAP components provide a connection from an external SWD probe to a standard RISC-V Debug Module with an APB Debug Module Interface. This Debug Module can then debug multiple downstream RISC-V cores. In RISC-V terms, the OpenDAP components are functioning as a Debug Transport Module. This scheme is compliant with the RISC-V debug specification. 19 | 20 | In this system, the RISC-V cores provide debugger access to system memory. An additional Mem-AP could be added to the DAP, to support direct memory access from the debugger without involving the RISC-V debug subsystem. Custom APs can also be added, for example to force the system clock tree into a known state and help diagnose issues where the core debug can't be accessed. 21 | 22 | ## Licensing 23 | 24 | The contents of this repository is licensed under CC0 1.0 Universal, which is similar to a public domain dedication. I wrote all of the code in this repository with reference to the [ADIv5.2 specification](https://developer.arm.com/documentation/ihi0031/latest/) for my own education and better understanding of the specification. I hope that publishing this RTL will help others to understand parts of the specification that I struggled with. 25 | 26 | Note that while this licence covers my own original Verilog source code, contained in this repository, it doesn't cover any underlying Arm intellectual property in ADIv5.2 or related Arm specifications. Seek legal advice, and ideally speak with your Arm handler, before deploying this RTL. Note also that I have no legal training, and the contents of this README does not constitute legal advice. 27 | 28 | ## TODO 29 | 30 | * Consider using APB3 for DP->AP interface 31 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_readok.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | 4 | // Test intent: check that CTRL/STAT.READOK is set/cleared in line with 5 | // APACC/RDBUFF read responses. 6 | 7 | bool ap_gives_err = false; 8 | ap_read_response read_callback(uint16_t addr) { 9 | return { 10 | .rdata = 0xcafef00du, 11 | .delay_cycles = 300, 12 | .err = ap_gives_err 13 | }; 14 | } 15 | 16 | int main() { 17 | tb t("waves.vcd"); 18 | t.set_ap_read_callback(read_callback); 19 | 20 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 21 | tb_assert(status == OK, "Failed to connect to DP\n"); 22 | 23 | uint32_t data; 24 | status = swd_read(t, AP, 0, data); 25 | tb_assert(status == OK, "Priming read should give OK\n"); 26 | 27 | // Check WAIT and FAULT for APACC 28 | 29 | const uint32_t READOK_MASK = 0x40u; 30 | idle_clocks(t, 500); 31 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 32 | tb_assert(status == OK, "Must get OK to CTRL/STAT read.\n"); 33 | tb_assert(data & READOK_MASK, "Priming AP read should have set READOK\n"); 34 | 35 | status = swd_read(t, AP, 0, data); 36 | tb_assert(status == OK, "Second priming read should give OK\n"); 37 | status = swd_read(t, AP, 0, data); 38 | tb_assert(status == WAIT, "AP read during stall should WAIT\n"); 39 | 40 | idle_clocks(t, 500); 41 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 42 | tb_assert(status == OK, "CTRL/STAT read after WAIT should give OK\n"); 43 | tb_assert(!(data & READOK_MASK), "READOK should be cleared by AP read WAIT\n"); 44 | 45 | idle_clocks(t, 500); 46 | ap_gives_err = true; 47 | status = swd_read(t, AP, 0, data); 48 | tb_assert(status == OK, "Third priming AP read should give OK\n"); 49 | idle_clocks(t, 500); 50 | status = swd_read(t, AP, 0, data); 51 | tb_assert(status == FAULT, "AP read following error completion should give FAULT\n"); 52 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 53 | tb_assert(!(data & READOK_MASK), "READOK should be cleared on FAULT\n"); 54 | 55 | 56 | // Finally check that RDBUFF WAIT also clears READOK. 57 | 58 | ap_gives_err = false; 59 | (void)swd_write(t, DP, DP_REG_ABORT, 0x4); 60 | status = swd_read(t, AP, 0, data); 61 | tb_assert(status == OK, "AP read should return to OK after ABORT\n"); 62 | 63 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 64 | tb_assert(status == OK, "CTRL/STAT read should not WAIT even when AP stalled\n"); 65 | tb_assert(data & READOK_MASK, "READOK should still be set if AP is stalled but no WAIT has been issued\n"); 66 | 67 | status = swd_read(t, DP, DP_REG_RDBUF, data); 68 | tb_assert(status == WAIT, "RDBUF should WAIT on stall.\n"); 69 | idle_clocks(t, 500); 70 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 71 | tb_assert(status == OK, "CTRL/STAT should still be readable when WAIT clears\n"); 72 | tb_assert(!(data & READOK_MASK), "READOK should be cleared by RDBUF WAIT\n"); 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /test/common/include/swd_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class tb; 4 | 5 | // Constants 6 | 7 | static const uint32_t DPIDR_EXPECTED = 0xdeadbeefu; 8 | static const uint32_t TARGETID_EXPECTED = 0xbaadf00du; 9 | // REVISION = 0, DESIGNER = 7ff, CLASS = Mem-AP, TYPE = APB2/APB3 10 | static const uint32_t APIDR_EXPECTED = 0x0fff0002u; 11 | 12 | enum ap_dp_t { 13 | DP = 0, 14 | AP = 1 15 | }; 16 | 17 | enum swd_status_t { 18 | OK = 1, 19 | WAIT = 2, 20 | FAULT = 4, 21 | DISCONNECTED = 7 22 | }; 23 | 24 | static const int DP_REG_DPIDR = 0; 25 | static const int DP_REG_ABORT = 0; 26 | static const int DP_REG_CTRL_STAT = 1; 27 | static const int DP_REG_DLCR = 1; 28 | static const int DP_REG_TARGETID = 1; 29 | static const int DP_REG_DLPIDR = 1; 30 | static const int DP_REG_EVENTSTAT = 1; 31 | static const int DP_REG_RESEND = 2; 32 | static const int DP_REG_SELECT = 2; 33 | static const int DP_REG_RDBUF = 3; 34 | static const int DP_REG_TARGETSEL = 3; 35 | 36 | static const int DP_BANK_CTRL_STAT = 0; 37 | static const int DP_BANK_DLCR = 1; 38 | static const int DP_BANK_TARGETID = 2; 39 | static const int DP_BANK_DLPIDR = 3; 40 | static const int DP_BANK_EVENTSTAT = 4; 41 | 42 | static const uint32_t DP_CTRL_STAT_CSYSPWRUPACK = 1u << 31; 43 | static const uint32_t DP_CTRL_STAT_CSYSPWRUPREQ = 1u << 30; 44 | static const uint32_t DP_CTRL_STAT_CDBGPWRUPACK = 1u << 29; 45 | static const uint32_t DP_CTRL_STAT_CDBGPWRUPREQ = 1u << 28; 46 | static const uint32_t DP_CTRL_STAT_CDBGRSTACK = 1u << 27; 47 | static const uint32_t DP_CTRL_STAT_CDBGRSTREQ = 1u << 26; 48 | static const uint32_t DP_CTRL_STAT_WDATAERR = 1u << 7; 49 | static const uint32_t DP_CTRL_STAT_READOK = 1u << 6; 50 | static const uint32_t DP_CTRL_STAT_STICKYERR = 1u << 5; 51 | static const uint32_t DP_CTRL_STAT_STICKYCMP = 1u << 4; 52 | static const uint32_t DP_CTRL_STAT_STICKYORUN = 1u << 1; 53 | static const uint32_t DP_CTRL_STAT_ORUNDETECT = 1u << 0; 54 | 55 | static const int AP_REG_CSW = 0; 56 | static const int AP_REG_TAR = 1; 57 | static const int AP_REG_DRW = 3; 58 | static const int AP_REG_CFG = 0; 59 | static const int AP_REG_BASE = 2; 60 | static const int AP_REG_IDR = 3; 61 | 62 | static const int AP_BANK_CSW = 0 << 4; 63 | static const int AP_BANK_TAR = 0 << 4; 64 | static const int AP_BANK_DRW = 0 << 4; 65 | static const int AP_BANK_BDx = 1 << 4; 66 | static const int AP_BANK_CFG = 0xf << 4; 67 | static const int AP_BANK_BASE = 0xf << 4; 68 | static const int AP_BANK_IDR = 0xf << 4; 69 | 70 | // Convenience functions 71 | 72 | void put_bits(tb &t, const uint8_t *tx, int n_bits); 73 | void get_bits(tb &t, uint8_t *rx, int n_bits); 74 | void hiz_clocks(tb &t, int n_bits); 75 | void idle_clocks(tb &t, int n_bits); 76 | 77 | uint8_t swd_header(ap_dp_t ap_ndp, bool read_nwrite, uint8_t addr); 78 | 79 | void send_dormant_to_swd(tb &t); 80 | void swd_line_reset(tb &t); 81 | void swd_targetsel(tb &t, uint32_t id); 82 | 83 | swd_status_t swd_read(tb &t, ap_dp_t ap_dp, uint8_t addr, uint32_t &data); 84 | swd_status_t swd_write(tb &t, ap_dp_t ap_dp, uint8_t addr, uint32_t data); 85 | swd_status_t swd_read_orun(tb &t, ap_dp_t ap_dp, uint8_t addr, uint32_t &data); 86 | swd_status_t swd_write_orun(tb &t, ap_dp_t ap_dp, uint8_t addr, uint32_t data); 87 | 88 | swd_status_t swd_prepare_dp_for_ap_access(tb &t); 89 | -------------------------------------------------------------------------------- /test/dap/tb/dap_integration.v: -------------------------------------------------------------------------------- 1 | // Integrate SW-DP and APB3 Mem-AP for testing. Actual testbench logic is all C++. 2 | 3 | module dap_integration #( 4 | parameter DPIDR = 32'hdeadbeef, 5 | parameter TARGETID = 32'hbaadf00d, 6 | 7 | parameter [10:0] IDR_DESIGNER = 11'h7ff, 8 | parameter [3:0] IDR_REVISION = 4'h0, 9 | parameter [31:0] BASE = 32'h0000_0000, 10 | parameter TAR_INCREMENT_BITS = 12 11 | 12 | ) ( 13 | 14 | input wire swclk, 15 | input wire rst_n, 16 | 17 | input wire swdi, 18 | output reg swdo, 19 | output reg swdo_en, 20 | 21 | output wire cdbgpwrupreq, 22 | input wire cdbgpwrupack, 23 | output wire csyspwrupreq, 24 | input wire csyspwrupack, 25 | 26 | input wire [3:0] instid, 27 | input wire eventstat, 28 | 29 | output wire dst_psel, 30 | output wire dst_penable, 31 | output wire dst_pwrite, 32 | output wire [31:0] dst_paddr, 33 | output wire [31:0] dst_pwdata, 34 | input wire [31:0] dst_prdata, 35 | input wire dst_pready, 36 | input wire dst_pslverr 37 | ); 38 | 39 | wire cdbgpwrupreq; 40 | wire cdbgpwrupack = cdbgpwrupreq; 41 | wire csyspwrupreq; 42 | wire csyspwrupack = csyspwrupreq; 43 | wire cdbgrstreq; 44 | wire cdbgrstack = cdbgrstreq; 45 | 46 | wire [7:0] ap_sel; 47 | wire [5:0] ap_addr; 48 | wire [31:0] ap_wdata; 49 | wire ap_wen; 50 | wire ap_ren; 51 | wire ap_abort; 52 | wire [31:0] ap_rdata; 53 | wire ap_rdy; 54 | wire ap_err; 55 | 56 | opendap_sw_dp #( 57 | .DPIDR (DPIDR), 58 | .TARGETID (TARGETID) 59 | ) dp ( 60 | .swclk (swclk), 61 | .rst_n (rst_n), 62 | 63 | .swdi (swdi), 64 | .swdo (swdo), 65 | .swdo_en (swdo_en), 66 | 67 | .cdbgpwrupreq (cdbgpwrupreq), 68 | .cdbgpwrupack (cdbgpwrupack), 69 | .csyspwrupreq (csyspwrupreq), 70 | .csyspwrupack (csyspwrupack), 71 | .cdbgrstreq (cdbgrstreq), 72 | .cdbgrstack (cdbgrstack), 73 | 74 | .instid (instid), 75 | .eventstat (eventstat), 76 | 77 | .ap_sel (ap_sel), 78 | .ap_addr (ap_addr), 79 | .ap_wdata (ap_wdata), 80 | .ap_wen (ap_wen), 81 | .ap_ren (ap_ren), 82 | .ap_abort (ap_abort), 83 | .ap_rdata (ap_rdata), 84 | .ap_rdy (ap_rdy), 85 | .ap_err (ap_err) 86 | ); 87 | 88 | opendap_mem_ap_apb #( 89 | .IDR_DESIGNER (IDR_DESIGNER), 90 | .IDR_REVISION (IDR_REVISION), 91 | .BASE (BASE), 92 | .TAR_INCREMENT_BITS (TAR_INCREMENT_BITS) 93 | ) ap ( 94 | .swclk (swclk), 95 | .rst_n_por (rst_n), 96 | 97 | // For simplicity, tie the clocks together. This CDC logic has already 98 | // been tested with JTAG on a RISC-V FPGA platform. 99 | .clk_dst (swclk), 100 | .rst_n_dst (rst_n), 101 | 102 | .dpacc_addr (ap_addr), 103 | .dpacc_wdata (ap_wdata), 104 | .dpacc_wen (ap_wen && ap_sel == 8'h00), 105 | .dpacc_ren (ap_ren && ap_sel == 8'h00), 106 | .dpacc_abort (ap_abort), 107 | .dpacc_rdata (ap_rdata), 108 | .dpacc_rdy (ap_rdy), 109 | .dpacc_err (ap_err), 110 | 111 | .dst_psel (dst_psel), 112 | .dst_penable (dst_penable), 113 | .dst_pwrite (dst_pwrite), 114 | .dst_paddr (dst_paddr), 115 | .dst_pwdata (dst_pwdata), 116 | .dst_prdata (dst_prdata), 117 | .dst_pready (dst_pready), 118 | .dst_pslverr (dst_pslverr) 119 | ); 120 | 121 | endmodule 122 | -------------------------------------------------------------------------------- /test/dp/testcase/ap_write_orundetect.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | #include 3 | #include 4 | 5 | // Test intent: check that a sequence of stalling AP writes with ORUNDETECT 6 | // set gives the correct OK -> WAIT -> FAULT sequence. 7 | 8 | std::vector write_history; 9 | 10 | ap_write_response write_callback(uint16_t addr, uint32_t data) { 11 | write_history.push_back((uint64_t)addr << 32 | data); 12 | return { 13 | .delay_cycles = 500, 14 | .err = false 15 | }; 16 | } 17 | 18 | const uint32_t MASK_ORUNDETECT = 0x1; 19 | const uint32_t MASK_STICKYORUN = 0x2; 20 | const uint32_t MASK_ORUNERRCLR = 0x10; 21 | 22 | int main() { 23 | tb t("waves.vcd"); 24 | t.set_ap_write_callback(write_callback); 25 | 26 | swd_status_t status = swd_prepare_dp_for_ap_access(t); 27 | tb_assert(status == OK, "Failed to connect to DP\n"); 28 | 29 | (void)swd_write(t, DP, DP_REG_CTRL_STAT, MASK_ORUNDETECT); 30 | uint32_t data; 31 | (void)swd_read(t, DP, DP_REG_CTRL_STAT, data); 32 | tb_assert(data & MASK_ORUNDETECT, "Failed to set CTRL/STAT.ORUNDETECT.\n"); 33 | 34 | const uint32_t magic = 0xffff1234; 35 | std::vector expected_write_seq; 36 | uint ctr = 0; 37 | 38 | status = swd_write_orun(t, AP, ctr & 0x3, magic + ctr); 39 | expected_write_seq.push_back((uint64_t)(ctr & 0x3) << 32 | magic + ctr); 40 | ++ctr; 41 | tb_assert(status == OK, "First write should give OK\n"); 42 | status = swd_write_orun(t, AP, ctr & 0x3, magic + ctr); 43 | ++ctr; 44 | tb_assert(status == WAIT, "Second write should give WAIT\n"); 45 | status = swd_write_orun(t, AP, ctr & 0x3, magic + ctr); 46 | ++ctr; 47 | tb_assert(status == FAULT, "Third write should give WAIT\n"); 48 | 49 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 50 | tb_assert(status == OK, "CTRL/STAT read during stall + overrun should still give OK\n"); 51 | tb_assert(data & MASK_STICKYORUN, "STICKYORUN should have been set due to WAIT/FAULT\n"); 52 | status = swd_write_orun(t, DP, DP_REG_ABORT, MASK_ORUNERRCLR); 53 | tb_assert(status == OK, "ABORT failed\n"); 54 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 55 | tb_assert(status == OK && !(data & MASK_STICKYORUN), "STICKYORUN clear failed\n"); 56 | 57 | status = swd_write_orun(t, AP, ctr & 0x3, magic + ctr); 58 | ++ctr; 59 | tb_assert(status == WAIT, "Response should return to WAIT after clearing STICKYORUN\n"); 60 | 61 | // Do the ORUN clear thing again. 62 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 63 | tb_assert(status == OK, "CTRL/STAT read during stall + overrun should still give OK\n"); 64 | tb_assert(data & MASK_STICKYORUN, "STICKYORUN should have been set due to WAIT/FAULT\n"); 65 | status = swd_write_orun(t, DP, DP_REG_ABORT, MASK_ORUNERRCLR); 66 | tb_assert(status == OK, "ABORT failed\n"); 67 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 68 | tb_assert(status == OK && !(data & MASK_STICKYORUN), "STICKYORUN clear failed\n"); 69 | 70 | idle_clocks(t, 200); 71 | 72 | status = swd_write_orun(t, AP, ctr & 0x3, magic + ctr); 73 | expected_write_seq.push_back((uint64_t)(ctr & 0x3) << 32 | magic + ctr); 74 | ++ctr; 75 | tb_assert(status == OK, "Write should give OK again after stall clears.\n"); 76 | status = swd_read_orun(t, DP, DP_REG_CTRL_STAT, data); 77 | tb_assert(status == OK && !(data & MASK_STICKYORUN), "STICKYORUN should not have been set by non-WAIT stalled transfer\n"); 78 | 79 | tb_assert( 80 | write_history.size() >= expected_write_seq.size(), 81 | "Not enough write data received, expected %lu, got %lu\n", 82 | expected_write_seq.size(), write_history.size() 83 | ); 84 | tb_assert( 85 | write_history.size() <= expected_write_seq.size(), 86 | "Too much write data received, expected %lu, got %lu\n", 87 | expected_write_seq.size(), write_history.size() 88 | ); 89 | for (size_t i = 0; i < expected_write_seq.size(); ++i){ 90 | tb_assert( 91 | write_history[i] == expected_write_seq[i], 92 | "Bad data item %lu, expected %012lx, got %012lx\n", 93 | i, expected_write_seq[i], write_history[i] 94 | ); 95 | } 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /test/dap/tb/tb.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "dut.cpp" 7 | #include 8 | 9 | tb::tb(std::string vcdfile) { 10 | cxxrtl_design::p_dap__integration *dap = new cxxrtl_design::p_dap__integration; 11 | dut = dap; 12 | 13 | waves_fd.open(vcdfile); 14 | cxxrtl::debug_items all_debug_items; 15 | dap->debug_info(all_debug_items); 16 | vcd.timescale(1, "us"); 17 | vcd.add(all_debug_items); 18 | vcd_sample = 0; 19 | 20 | dap->p_rst__n.set(false); 21 | dap->step(); 22 | dap->p_rst__n.set(true); 23 | dap->p_dst__pready.set(true); 24 | dap->step(); 25 | 26 | swclk_prev = false; 27 | read_callback = NULL; 28 | write_callback = NULL; 29 | last_read_response.delay_cycles = 0; 30 | last_write_response.delay_cycles = 0; 31 | 32 | vcd.sample(vcd_sample++); 33 | waves_fd << vcd.buffer; 34 | vcd.buffer.clear(); 35 | } 36 | 37 | void tb::set_apb_read_callback(apb_read_callback cb) { 38 | read_callback = cb; 39 | } 40 | 41 | void tb::set_apb_write_callback(apb_write_callback cb) { 42 | write_callback = cb; 43 | } 44 | 45 | void tb::set_swclk(bool swclk) { 46 | static_cast(dut)->p_swclk.set(swclk); 47 | } 48 | 49 | void tb::set_swdi(bool swdi) { 50 | static_cast(dut)->p_swdi.set(swdi); 51 | } 52 | 53 | bool tb::get_swdo() { 54 | // Pullup on bus, so return 1 if pin tristated. 55 | return static_cast(dut)->p_swdo__en.get() ? 56 | static_cast(dut)->p_swdo.get() : true; 57 | } 58 | 59 | void tb::set_instid(uint8_t instid) { 60 | static_cast(dut)->p_instid.set(instid); 61 | } 62 | 63 | void tb::step() { 64 | cxxrtl_design::p_dap__integration *dp = static_cast(dut); 65 | 66 | // Respond only to setup phase, then assume that access phase happens. 67 | // Less state to track. 68 | bool apb_start = dp->p_dst__psel.get() && !dp->p_dst__penable.get(); 69 | uint32_t paddr = dp->p_dst__paddr.get(); 70 | bool pwrite = dp->p_dst__pwrite.get(); 71 | uint32_t pwdata = dp->p_dst__pwdata.get(); 72 | 73 | dp->step(); 74 | dp->step(); 75 | vcd.sample(vcd_sample++); 76 | waves_fd << vcd.buffer; 77 | waves_fd.flush(); 78 | vcd.buffer.clear(); 79 | 80 | // Field APB accesses using testcase callbacks if available, and provide 81 | // bus responses with correct timing based on callback results. 82 | if (!swclk_prev && dp->p_swclk.get()) { 83 | if (last_read_response.delay_cycles > 0) { 84 | --last_read_response.delay_cycles; 85 | if (last_read_response.delay_cycles == 0) { 86 | dp->p_dst__prdata.set(last_read_response.rdata); 87 | dp->p_dst__pslverr.set(last_read_response.err); 88 | dp->p_dst__pready.set(1); 89 | } 90 | } 91 | if (last_write_response.delay_cycles > 0) { 92 | --last_write_response.delay_cycles; 93 | if (last_write_response.delay_cycles == 0) { 94 | dp->p_dst__pslverr.set(last_write_response.err); 95 | dp->p_dst__pready.set(1); 96 | } 97 | } 98 | if (apb_start && !pwrite && read_callback) { 99 | last_read_response = read_callback(paddr); 100 | if (last_read_response.delay_cycles == 0) { 101 | dp->p_dst__prdata.set(last_read_response.rdata); 102 | dp->p_dst__pslverr.set(last_read_response.err); 103 | } 104 | else { 105 | dp->p_dst__pready.set(0); 106 | } 107 | } 108 | else if (apb_start && pwrite && write_callback) { 109 | last_write_response = write_callback(paddr, pwdata); 110 | if (last_write_response.delay_cycles == 0) { 111 | dp->p_dst__pslverr.set(last_write_response.err); 112 | } 113 | else { 114 | dp->p_dst__pready.set(0); 115 | } 116 | } 117 | } 118 | swclk_prev = dp->p_swclk.get(); 119 | } 120 | -------------------------------------------------------------------------------- /test/dp/tb/tb.cpp: -------------------------------------------------------------------------------- 1 | #include "tb.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "dut.cpp" 7 | #include 8 | 9 | tb::tb(std::string vcdfile) { 10 | // Raw pointer... CXXRTL doesn't give us the type declaration without also 11 | // giving us non-inlined implementation, and I'm not very good at C++, so 12 | // we do this shit 13 | cxxrtl_design::p_opendap__sw__dp *dp = new cxxrtl_design::p_opendap__sw__dp; 14 | dut = dp; 15 | 16 | waves_fd.open(vcdfile); 17 | cxxrtl::debug_items all_debug_items; 18 | dp->debug_info(all_debug_items); 19 | vcd.timescale(1, "us"); 20 | vcd.add(all_debug_items); 21 | vcd_sample = 0; 22 | 23 | dp->p_rst__n.set(false); 24 | dp->step(); 25 | dp->p_rst__n.set(true); 26 | dp->p_ap__rdy.set(true); 27 | dp->step(); 28 | 29 | swclk_prev = false; 30 | read_callback = NULL; 31 | write_callback = NULL; 32 | last_read_response.delay_cycles = 0; 33 | last_write_response.delay_cycles = 0; 34 | 35 | vcd.sample(vcd_sample++); 36 | waves_fd << vcd.buffer; 37 | vcd.buffer.clear(); 38 | } 39 | 40 | void tb::set_ap_read_callback(ap_read_callback cb) { 41 | read_callback = cb; 42 | } 43 | 44 | void tb::set_ap_write_callback(ap_write_callback cb) { 45 | write_callback = cb; 46 | } 47 | 48 | void tb::set_swclk(bool swclk) { 49 | static_cast(dut)->p_swclk.set(swclk); 50 | } 51 | 52 | void tb::set_swdi(bool swdi) { 53 | static_cast(dut)->p_swdi.set(swdi); 54 | } 55 | 56 | bool tb::get_swdo() { 57 | // Pullup on bus, so return 1 if pin tristated. 58 | return static_cast(dut)->p_swdo__en.get() ? 59 | static_cast(dut)->p_swdo.get() : true; 60 | } 61 | 62 | void tb::set_instid(uint8_t instid) { 63 | static_cast(dut)->p_instid.set(instid); 64 | } 65 | 66 | void tb::step() { 67 | cxxrtl_design::p_opendap__sw__dp *dp = static_cast(dut); 68 | 69 | uint16_t ap_addr = dp->p_ap__addr.get() | dp->p_ap__sel.get() << 6; 70 | bool ap_wen = dp->p_ap__wen.get(); 71 | bool ap_ren = dp->p_ap__ren.get(); 72 | uint32_t ap_wdata = dp->p_ap__wdata.get(); 73 | 74 | dp->step(); 75 | dp->step(); 76 | vcd.sample(vcd_sample++); 77 | waves_fd << vcd.buffer; 78 | waves_fd.flush(); 79 | vcd.buffer.clear(); 80 | 81 | // Field AP accesses using testcase callbacks if available, and provide AP 82 | // bus responses with correct timing based on callback results. 83 | if (!swclk_prev && dp->p_swclk.get()) { 84 | dp->p_ap__err.set(0); 85 | if (last_read_response.delay_cycles > 0) { 86 | --last_read_response.delay_cycles; 87 | if (last_read_response.delay_cycles == 0) { 88 | dp->p_ap__rdata.set(last_read_response.rdata); 89 | dp->p_ap__err.set(last_read_response.err); 90 | dp->p_ap__rdy.set(1); 91 | } 92 | } 93 | if (last_write_response.delay_cycles > 0) { 94 | --last_write_response.delay_cycles; 95 | if (last_write_response.delay_cycles == 0) { 96 | dp->p_ap__err.set(last_write_response.err); 97 | dp->p_ap__rdy.set(1); 98 | } 99 | } 100 | if (ap_ren && read_callback) { 101 | last_read_response = read_callback(ap_addr); 102 | if (last_read_response.delay_cycles == 0) { 103 | dp->p_ap__rdata.set(last_read_response.rdata); 104 | dp->p_ap__err.set(last_read_response.err); 105 | } 106 | else { 107 | dp->p_ap__rdy.set(0); 108 | } 109 | } 110 | else if (ap_wen && write_callback) { 111 | last_write_response = write_callback(ap_addr, ap_wdata); 112 | if (last_write_response.delay_cycles == 0) { 113 | dp->p_ap__err.set(last_write_response.err); 114 | } 115 | else { 116 | dp->p_ap__rdy.set(0); 117 | } 118 | } 119 | } 120 | swclk_prev = dp->p_swclk.get(); 121 | 122 | // Tie back REQs to ACKs so they can be toggled 123 | dp->p_csyspwrupack.set(dp->p_csyspwrupreq.get()); 124 | dp->p_cdbgpwrupack.set(dp->p_cdbgpwrupreq.get()); 125 | dp->p_cdbgrstack.set(dp->p_cdbgrstreq.get()); 126 | } 127 | -------------------------------------------------------------------------------- /hdl/opendap_swd_dormant_monitor.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // Watch for switches between the Dormant and SWD link states. 7 | // Ref: IHI0031F, "B5.3 Dormant Operation" 8 | 9 | `default_nettype none 10 | 11 | module opendap_swd_dormant_monitor ( 12 | input wire swclk, 13 | input wire rst_n, 14 | 15 | input wire swdi_reg, 16 | 17 | output reg exit_dormant, 18 | output reg enter_dormant, 19 | output reg line_reset 20 | ); 21 | 22 | // Magic numbers from the spec 23 | localparam [7:0] SELECT_D2S = 8'b01011000; 24 | localparam [15:0] SELECT_S2D = 16'b00111101_11000111; 25 | localparam [6:0] LFSR_INIT = 7'b1001001; 26 | localparam [6:0] LFSR_TAPS = 7'b1001011; 27 | 28 | // The "activation select" sequence is a zero bit followed by 127 cycles from 29 | // this magic LFSR. We resync the LFSR whenever we see a sequence mismatch. 30 | 31 | reg [6:0] lfsr; 32 | reg lfsr_resync; 33 | 34 | always @ (posedge swclk or negedge rst_n) begin 35 | if (!rst_n) begin 36 | lfsr <= LFSR_INIT; 37 | end else if (lfsr_resync) begin 38 | lfsr <= LFSR_INIT; 39 | end else begin 40 | lfsr <= {^(lfsr & LFSR_TAPS), lfsr[6:1]}; 41 | end 42 | end 43 | 44 | localparam W_STATE = 3; 45 | localparam S_D2S_START_BIT = 3'd0; 46 | localparam S_D2S_ALERT = 3'd1; 47 | localparam S_D2S_POSTALERT = 3'd2; 48 | localparam S_D2S_SELECT = 3'd3; 49 | localparam S_S2D_RESET_HIGH = 3'd4; 50 | localparam S_S2D_RESET_LOW1 = 3'd5; 51 | localparam S_S2D_RESET_LOW2 = 3'd6; 52 | localparam S_S2D_SELECT = 3'd7; 53 | 54 | reg [6:0] bit_ctr, bit_ctr_nxt; 55 | reg [5:0] rst_ctr, rst_ctr_nxt; 56 | reg [W_STATE-1:0] state, state_nxt; 57 | 58 | always @ (*) begin 59 | state_nxt = state; 60 | bit_ctr_nxt = bit_ctr - |bit_ctr; 61 | rst_ctr_nxt = swdi_reg ? rst_ctr - |rst_ctr : 6'd50; 62 | 63 | enter_dormant = 1'b0; 64 | exit_dormant = 1'b0; 65 | line_reset = 1'b0; 66 | lfsr_resync = 1'b1; 67 | 68 | case (state) 69 | S_D2S_START_BIT: begin 70 | bit_ctr_nxt = 7'd126; 71 | if (!swdi_reg) begin 72 | state_nxt = S_D2S_ALERT; 73 | end 74 | end 75 | S_D2S_ALERT: begin 76 | if (swdi_reg == lfsr[0]) begin 77 | lfsr_resync = 1'b0; 78 | if (~|bit_ctr) begin 79 | bit_ctr_nxt = 7'd3; 80 | state_nxt = S_D2S_POSTALERT; 81 | end 82 | end else begin 83 | bit_ctr_nxt = 7'd126; 84 | state_nxt = swdi_reg ? S_D2S_START_BIT : S_D2S_ALERT; 85 | end 86 | end 87 | S_D2S_POSTALERT: begin 88 | // Ignore 4 cycles whilst host drives SWDIO low. 89 | if (~|bit_ctr) begin 90 | bit_ctr_nxt = 7'd7; 91 | state_nxt = S_D2S_SELECT; 92 | end 93 | end 94 | S_D2S_SELECT: begin 95 | if (swdi_reg == SELECT_D2S[bit_ctr]) begin 96 | if (~|bit_ctr) begin 97 | exit_dormant = 1'b1; 98 | state_nxt = S_S2D_RESET_HIGH; 99 | rst_ctr_nxt = 7'd49; 100 | end 101 | end else begin 102 | bit_ctr_nxt = 7'd126; 103 | state_nxt = swdi_reg ? S_D2S_START_BIT : S_D2S_ALERT; 104 | end 105 | end 106 | S_S2D_RESET_HIGH: begin 107 | // Note we are using two separate counters to handle the case where a 108 | // reset may be part of a partially-successful but ultimately failing 109 | // dormant select sequence. 110 | if (~|rst_ctr_nxt) 111 | state_nxt = S_S2D_RESET_LOW1; 112 | end 113 | S_S2D_RESET_LOW1: begin 114 | // More than 50 cycles high is legal. We sit waiting for a low. 115 | if (!swdi_reg) 116 | state_nxt = S_S2D_RESET_LOW2; 117 | end 118 | S_S2D_RESET_LOW2: begin 119 | // The first low cycle must be followed by a second one, else it's not 120 | // a legal reset sequence, and we need 50 cycles high again. 121 | if (swdi_reg) begin 122 | state_nxt = S_S2D_RESET_HIGH; 123 | end else begin 124 | line_reset = 1'b1; 125 | state_nxt = S_S2D_SELECT; 126 | // First two bits of "select" are actually the two 0 bits on reset. 127 | bit_ctr_nxt = 7'd13; 128 | end 129 | end 130 | S_S2D_SELECT: begin 131 | if (swdi_reg == SELECT_S2D[bit_ctr]) begin 132 | if (~|bit_ctr) begin 133 | enter_dormant = 1'b1; 134 | state_nxt = S_D2S_START_BIT; 135 | end 136 | end else begin 137 | // No match, go back to waiting for reset. (The reset counter has 138 | // been tracking 1 bits in this potential select sequence the 139 | // whole time, so we may now spend fewer than 50 cycles in the 140 | // RESET_HIGH state.) 141 | state_nxt = S_S2D_RESET_HIGH; 142 | end 143 | end 144 | default: begin 145 | state_nxt = S_D2S_START_BIT; 146 | end 147 | endcase 148 | end 149 | 150 | always @ (posedge swclk or negedge rst_n) begin 151 | if (!rst_n) begin 152 | state <= S_D2S_START_BIT; 153 | bit_ctr <= 7'd0; 154 | rst_ctr <= 6'd0; 155 | end else begin 156 | state <= state_nxt; 157 | bit_ctr <= bit_ctr_nxt; 158 | rst_ctr <= rst_ctr_nxt; 159 | end 160 | end 161 | 162 | endmodule 163 | 164 | `ifndef YOSYS 165 | `default_nettype wire 166 | `endif 167 | -------------------------------------------------------------------------------- /example/fpga/fpga_icebreaker.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project (c) Luke Wren 2021 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // Example DP instantiation top-level on iCEBreaker FPGA board. 7 | 8 | `default_nettype none 9 | 10 | module fpga_icebreaker #( 11 | parameter DPIDR = 32'hdeadbeef, 12 | parameter TARGETID = 32'hbaadf00d, 13 | 14 | parameter [10:0] IDR_DESIGNER = 11'h7ff, 15 | parameter [3:0] IDR_REVISION = 4'h0, 16 | parameter [31:0] BASE = 32'h0000_0000, 17 | parameter TAR_INCREMENT_BITS = 12 18 | ) ( 19 | input wire swclk, 20 | inout wire swdio, 21 | 22 | // LED is connected to CDBGPWRUPREQ: 23 | output wire led 24 | ); 25 | 26 | // ---------------------------------------------------------------------------- 27 | // Power-on reset 28 | 29 | // The DAP's PoR reset is synchronous, but it is acceptable to synchronise 30 | // it -- the synchroniser will elapse during the host's initial mandatory 8 31 | // clocks with SWDIO high before Dormant-to-SWD is sent. 32 | 33 | wire rst_n_por; 34 | 35 | fpga_reset #( 36 | .SHIFT (2), 37 | .COUNT (0) 38 | ) rst_por_u ( 39 | .clk (swclk), 40 | .force_rst_n (1'b1), 41 | .rst_n (rst_n_por) 42 | ); 43 | 44 | // ---------------------------------------------------------------------------- 45 | // Debug Port instantiation 46 | 47 | wire swdi; 48 | wire swdo; 49 | wire swdo_en; 50 | 51 | wire cdbgpwrupreq; 52 | wire cdbgpwrupack = cdbgpwrupreq; 53 | wire csyspwrupreq; 54 | wire csyspwrupack = csyspwrupreq; 55 | wire cdbgrstreq; 56 | wire cdbgrstack = cdbgrstreq; 57 | 58 | // Note eventstat is an active-low event signal, so tied high if unused. 59 | wire [3:0] instid = 4'h0; 60 | wire eventstat = 1'b1; 61 | 62 | wire [7:0] ap_sel; 63 | wire [5:0] ap_addr; 64 | wire [31:0] ap_wdata; 65 | wire ap_wen; 66 | wire ap_ren; 67 | wire ap_abort; 68 | wire [31:0] ap_rdata; 69 | wire ap_rdy; 70 | wire ap_err; 71 | 72 | opendap_sw_dp #( 73 | .DPIDR (DPIDR), 74 | .TARGETID (TARGETID) 75 | ) dp ( 76 | .swclk (swclk), 77 | .rst_n (rst_n_por), 78 | 79 | .swdi (swdi), 80 | .swdo (swdo), 81 | .swdo_en (swdo_en), 82 | 83 | .cdbgpwrupreq (cdbgpwrupreq), 84 | .cdbgpwrupack (cdbgpwrupack), 85 | .csyspwrupreq (csyspwrupreq), 86 | .csyspwrupack (csyspwrupack), 87 | .cdbgrstreq (cdbgrstreq), 88 | .cdbgrstack (cdbgrstack), 89 | 90 | .instid (instid), 91 | .eventstat (eventstat), 92 | 93 | .ap_sel (ap_sel), 94 | .ap_addr (ap_addr), 95 | .ap_wdata (ap_wdata), 96 | .ap_wen (ap_wen), 97 | .ap_ren (ap_ren), 98 | .ap_abort (ap_abort), 99 | .ap_rdata (ap_rdata), 100 | .ap_rdy (ap_rdy), 101 | .ap_err (ap_err) 102 | ); 103 | 104 | // Mandatory pullup on SWDIO, so need to instantiate a pad: 105 | 106 | SB_IO #( 107 | .PIN_TYPE (6'b10_10_01), 108 | // | | | 109 | // | | \----- Unregistered input 110 | // | \-------- Unregistered output 111 | // \----------- Unregistered output enable 112 | .PULLUP (1'b1) 113 | ) input_buffer ( 114 | .D_OUT_0 (swdo), 115 | .D_IN_0 (swdi), 116 | .OUTPUT_ENABLE (swdo_en), 117 | .PACKAGE_PIN (swdio) 118 | ); 119 | 120 | // Active-low LED 121 | // assign led = !cdbgpwrupreq; 122 | 123 | // ---------------------------------------------------------------------------- 124 | // Access port instantiation: 125 | 126 | // This is in general asynchronous, but in the interest of giving it SOME clock: 127 | wire clk_dst = swclk; 128 | wire rst_n_dst = rst_n_por; 129 | 130 | wire ap0_wen = ap_wen && ap_sel == 8'h00; 131 | wire ap0_ren = ap_ren && ap_sel == 8'h00; 132 | wire [31:0] ap0_rdata; 133 | 134 | reg read_selected_ap0; 135 | always @ (posedge swclk or negedge rst_n_por) begin 136 | if (!rst_n_por) begin 137 | read_selected_ap0 <= 1'b0; 138 | end else if (ap_ren) begin 139 | read_selected_ap0 <= ap_sel == 8'h00; 140 | end 141 | end 142 | 143 | assign led = !read_selected_ap0; 144 | 145 | assign ap_rdata = read_selected_ap0 ? ap0_rdata : 32'h0; 146 | 147 | wire dst_psel; 148 | wire dst_penable; 149 | wire dst_pwrite; 150 | wire [31:0] dst_paddr; 151 | wire [31:0] dst_pwdata; 152 | wire [31:0] dst_prdata; 153 | wire dst_pready; 154 | wire dst_pslverr; 155 | 156 | opendap_mem_ap_apb #( 157 | .IDR_DESIGNER (IDR_DESIGNER), 158 | .IDR_REVISION (IDR_REVISION), 159 | .BASE (BASE), 160 | .TAR_INCREMENT_BITS (TAR_INCREMENT_BITS) 161 | ) ap ( 162 | .swclk (swclk), 163 | .rst_n_por (rst_n_por), 164 | 165 | .clk_dst (clk_dst), 166 | .rst_n_dst (rst_n_dst), 167 | 168 | .dpacc_addr (ap_addr), 169 | .dpacc_wdata (ap_wdata), 170 | .dpacc_wen (ap0_wen), 171 | .dpacc_ren (ap0_ren), 172 | .dpacc_abort (ap_abort), 173 | .dpacc_rdata (ap0_rdata), 174 | .dpacc_rdy (ap_rdy), 175 | .dpacc_err (ap_err), 176 | 177 | .dst_psel (dst_psel), 178 | .dst_penable (dst_penable), 179 | .dst_pwrite (dst_pwrite), 180 | .dst_paddr (dst_paddr), 181 | .dst_pwdata (dst_pwdata), 182 | .dst_prdata (dst_prdata), 183 | .dst_pready (dst_pready), 184 | .dst_pslverr (dst_pslverr) 185 | ); 186 | 187 | // ---------------------------------------------------------------------------- 188 | // Dummy APB slave 189 | 190 | assign dst_prdata = dst_paddr; 191 | assign dst_pready = 1'b1; 192 | assign dst_pslverr = 1'b0; 193 | 194 | endmodule 195 | -------------------------------------------------------------------------------- /test/common/swd_util.cpp: -------------------------------------------------------------------------------- 1 | // Convenience functions 2 | 3 | // tb for whatever we are testing, which is expected to have functions for 4 | // stepping, setting/getting the SWD signals, etc. 5 | #include "tb.h" 6 | 7 | void put_bits(tb &t, const uint8_t *tx, int n_bits) { 8 | uint8_t shifter; 9 | for (int i = 0; i < n_bits; ++i) { 10 | if (i % 8 == 0) 11 | shifter = tx[i / 8]; 12 | else 13 | shifter >>= 1; 14 | t.set_swdi(shifter & 1u); 15 | t.step(); 16 | t.set_swclk(1); 17 | t.step(); 18 | t.set_swclk(0); 19 | } 20 | } 21 | 22 | void get_bits(tb &t, uint8_t *rx, int n_bits) { 23 | uint8_t shifter; 24 | for (int i = 0; i < n_bits; ++i) { 25 | t.step(); 26 | bool sample = t.get_swdo(); 27 | t.set_swdi(sample); 28 | t.set_swclk(1); 29 | t.step(); 30 | t.set_swclk(0); 31 | 32 | shifter = (shifter >> 1) | (sample << 7); 33 | if (i % 8 == 7) 34 | rx[i / 8] = shifter; 35 | } 36 | if (n_bits % 8 != 0) { 37 | rx[n_bits / 8] = shifter >> (8 - n_bits % 8); 38 | } 39 | } 40 | 41 | void hiz_clocks(tb &t, int n_bits) { 42 | for (int i = 0; i < n_bits; ++i) { 43 | t.set_swdi(t.get_swdo()); 44 | t.step(); 45 | t.set_swclk(1); 46 | t.step(); 47 | t.set_swclk(0); 48 | } 49 | } 50 | 51 | void idle_clocks(tb &t, int n_bits) { 52 | t.set_swdi(0); 53 | for (int i = 0; i < n_bits; ++i) { 54 | t.step(); 55 | t.set_swclk(1); 56 | t.step(); 57 | t.set_swclk(0); 58 | } 59 | } 60 | 61 | static const uint8_t seq_dormant_to_swd[] = { 62 | // Resync the LFSR (which is 7 bits, can't produce 8 1s) 63 | 0xff, 64 | // A 0-bit, then 127 bits of LFSR output 65 | 0x92, 0xf3, 0x09, 0x62, 66 | 0x95, 0x2d, 0x85, 0x86, 67 | 0xe9, 0xaf, 0xdd, 0xe3, 68 | 0xa2, 0x0e, 0xbc, 0x19, 69 | // Four zero-bits, 8 bits of select sequence 70 | 0xa0, 0x01 71 | // Total 148 bits. 72 | }; 73 | 74 | void send_dormant_to_swd(tb &t) { 75 | put_bits(t, seq_dormant_to_swd, 148); 76 | } 77 | 78 | static const uint8_t seq_line_reset[7] = { 79 | // 50 1s, at least 2 0s. 80 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03 81 | }; 82 | 83 | void swd_line_reset(tb &t) { 84 | put_bits(t, seq_line_reset, 52); 85 | } 86 | 87 | 88 | uint8_t swd_header(ap_dp_t ap_ndp, bool read_nwrite, uint8_t addr) { 89 | addr &= 0x3; 90 | return 91 | 1u << 0 | // Start 92 | (uint8_t)ap_ndp << 1 | // APnDP 93 | (uint8_t)read_nwrite << 2 | // RnW 94 | addr << 3 | // A[3:2] 95 | ((addr >> 1) ^ (addr & 1) ^ (uint8_t)read_nwrite ^ (uint8_t)ap_ndp) << 5 | // parity 96 | 0u << 6 | // Stop 97 | 1u << 7; // Park 98 | } 99 | 100 | void swd_targetsel(tb &t, uint32_t id) { 101 | uint8_t header = swd_header(DP, 0, 3); 102 | put_bits(t, &header, 8); 103 | // No response to TARGETSEL. 104 | hiz_clocks(t, 5); 105 | uint8_t txbuf[4]; 106 | for (int i = 0; i < 4; ++i) 107 | txbuf[i] = (id >> i * 8) & 0xff; 108 | put_bits(t, txbuf, 32); 109 | // Parity 110 | txbuf[0] = 0; 111 | for (int i = 0; i < 32; ++i) 112 | txbuf[0] ^= (id >> i) & 0x1; 113 | put_bits(t, txbuf, 1); 114 | } 115 | 116 | 117 | static inline swd_status_t swd_read_impl(tb &t, ap_dp_t ap_ndp, uint8_t addr, uint32_t &data, bool orundetect) { 118 | uint8_t header = swd_header(ap_ndp, 1, addr); 119 | put_bits(t, &header, 8); 120 | hiz_clocks(t, 1); 121 | uint8_t status; 122 | get_bits(t, &status, 3); 123 | if (status != OK && !orundetect) { 124 | hiz_clocks(t, 1); 125 | data = 0; 126 | return (swd_status_t)status; 127 | } 128 | uint8_t rxbuf[4]; 129 | get_bits(t, rxbuf, 32); 130 | data = 0; 131 | for (int i = 0; i < 4; ++i) 132 | data = (data >> 8) | ((uint32_t)rxbuf[i] << 24); 133 | // Just discard parity bit -- have a separate test for that. 134 | get_bits(t, rxbuf, 1); 135 | // Turnaround for next packet header 136 | hiz_clocks(t, 1); 137 | return (swd_status_t)status; 138 | } 139 | 140 | static inline swd_status_t swd_write_impl(tb &t, ap_dp_t ap_ndp, uint8_t addr, uint32_t data, bool orundetect) { 141 | uint8_t header = swd_header(ap_ndp, 0, addr); 142 | put_bits(t, &header, 8); 143 | hiz_clocks(t, 1); 144 | uint8_t status; 145 | get_bits(t, &status, 3); 146 | if (status != OK && !orundetect) { 147 | hiz_clocks(t, 1); 148 | data = 0; 149 | return (swd_status_t)status; 150 | } 151 | hiz_clocks(t, 1); 152 | uint8_t txbuf[4]; 153 | for (int i = 0; i < 4; ++i) 154 | txbuf[i] = (data >> i * 8) & 0xff; 155 | put_bits(t, txbuf, 32); 156 | // Parity 157 | txbuf[0] = 0; 158 | for (int i = 0; i < 32; ++i) 159 | txbuf[0] ^= (data >> i) & 0x1; 160 | put_bits(t, txbuf, 1); 161 | return (swd_status_t)status; 162 | } 163 | 164 | swd_status_t swd_read(tb &t, ap_dp_t ap_ndp, uint8_t addr, uint32_t &data) { 165 | return swd_read_impl(t, ap_ndp, addr, data, false); 166 | } 167 | 168 | swd_status_t swd_read_orun(tb &t, ap_dp_t ap_ndp, uint8_t addr, uint32_t &data) { 169 | return swd_read_impl(t, ap_ndp, addr, data, true); 170 | } 171 | 172 | swd_status_t swd_write(tb &t, ap_dp_t ap_ndp, uint8_t addr, uint32_t data) { 173 | return swd_write_impl(t, ap_ndp, addr, data, false); 174 | } 175 | 176 | swd_status_t swd_write_orun(tb &t, ap_dp_t ap_ndp, uint8_t addr, uint32_t data) { 177 | return swd_write_impl(t, ap_ndp, addr, data, true); 178 | } 179 | 180 | swd_status_t swd_prepare_dp_for_ap_access(tb &t) { 181 | send_dormant_to_swd(t); 182 | swd_line_reset(t); 183 | 184 | // DPIDR read required to leave Reset state 185 | uint32_t data; 186 | swd_status_t status = swd_read(t, DP, DP_REG_DPIDR, data); 187 | if (status != OK) { 188 | return status; 189 | } 190 | 191 | // Clear any outstanding errors via ABORT so that SELECT becomes writable 192 | // (See B4.2.8) 193 | status = swd_write(t, DP, DP_REG_ABORT, 0x1e); 194 | if (status != OK) { 195 | return status; 196 | } 197 | 198 | // Power up before attempting AP accesses 199 | status = swd_write(t, DP, DP_REG_SELECT, DP_BANK_CTRL_STAT); 200 | if (status != OK) { 201 | return status; 202 | } 203 | status = swd_write(t, DP, DP_REG_CTRL_STAT, DP_CTRL_STAT_CSYSPWRUPREQ | DP_CTRL_STAT_CDBGPWRUPREQ); 204 | if (status != OK) { 205 | return status; 206 | } 207 | // Check for ACK. Assume there is no delay (true in tb), so don't bother with poll timeout. 208 | status = swd_read(t, DP, DP_REG_CTRL_STAT, data); 209 | if (status != OK) { 210 | return status; 211 | } 212 | if ((data & (DP_CTRL_STAT_CSYSPWRUPACK | DP_CTRL_STAT_CDBGPWRUPACK)) != (DP_CTRL_STAT_CSYSPWRUPACK | DP_CTRL_STAT_CDBGPWRUPACK)) { 213 | return FAULT; 214 | } 215 | return OK; 216 | } 217 | 218 | -------------------------------------------------------------------------------- /hdl/opendap_apb_async_bridge.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // APB-to-APB asynchronous bridge for bottom side of Mem-AP. Based on the DTM 7 | // APB async bridge from Hazard3 (also my project, available under Apache 2.0). 8 | // 9 | // Note this module depends on the opendap_sync_1bit module (a flop-chain 10 | // synchroniser) which should be reimplemented for your FPGA/process. 11 | 12 | `ifndef OPENDAP_REG_KEEP_ATTRIBUTE 13 | `define OPENDAP_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *) 14 | `endif 15 | 16 | `default_nettype none 17 | 18 | module opendap_apb_async_bridge #( 19 | parameter W_ADDR = 8, 20 | parameter W_DATA = 32, 21 | parameter N_SYNC_STAGES = 2 22 | ) ( 23 | // Resets assumed to be synchronised externally 24 | input wire clk_src, 25 | input wire rst_n_src, 26 | 27 | input wire clk_dst, 28 | input wire rst_n_dst, 29 | 30 | // APB port from Transport Module 31 | input wire src_psel, 32 | input wire src_penable, 33 | input wire src_pwrite, 34 | input wire [W_ADDR-1:0] src_paddr, 35 | input wire [W_DATA-1:0] src_pwdata, 36 | output wire [W_DATA-1:0] src_prdata, 37 | output wire src_pready, 38 | output wire src_pslverr, 39 | 40 | // APB port to Debug Module 41 | output wire dst_psel, 42 | output wire dst_penable, 43 | output wire dst_pwrite, 44 | output wire [W_ADDR-1:0] dst_paddr, 45 | output wire [W_DATA-1:0] dst_pwdata, 46 | input wire [W_DATA-1:0] dst_prdata, 47 | input wire dst_pready, 48 | input wire dst_pslverr 49 | ); 50 | 51 | // ---------------------------------------------------------------------------- 52 | // Clock-crossing registers 53 | 54 | // We're using a modified req/ack handshake: 55 | // 56 | // - Initially both req and ack are low 57 | // - src asserts req high 58 | // - dst responds with ack high and begins transfer 59 | // - src deasserts req once it sees ack low 60 | // - dst deasserts ack once: 61 | // - transfer is complete *and* 62 | // - dst sees req deasserted 63 | // - Once src sees ack low, a new transfer can begin. 64 | // 65 | // A NRZI toggle handshake might be more appropriate, but can cause spurious 66 | // bus accesses when only one side of the link is reset. 67 | 68 | `OPENDAP_REG_KEEP_ATTRIBUTE reg src_req; 69 | wire dst_req; 70 | 71 | `OPENDAP_REG_KEEP_ATTRIBUTE reg dst_ack; 72 | wire src_ack; 73 | 74 | // Note the launch registers are not resettable. We maintain setup/hold on 75 | // launch-to-capture paths thanks to the req/ack handshake. A stray reset 76 | // could violate this. 77 | // 78 | // The req/ack logic itself can be reset safely because the receiving domain 79 | // is protected from metastability by a 2FF synchroniser. 80 | 81 | `OPENDAP_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] src_paddr_pwdata_pwrite; // launch 82 | `OPENDAP_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] dst_paddr_pwdata_pwrite; // capture 83 | 84 | `OPENDAP_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] dst_prdata_pslverr; // launch 85 | `OPENDAP_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] src_prdata_pslverr; // capture 86 | 87 | opendap_sync_1bit #( 88 | .N_STAGES (N_SYNC_STAGES) 89 | ) sync_req ( 90 | .clk (clk_dst), 91 | .rst_n (rst_n_dst), 92 | .i (src_req), 93 | .o (dst_req) 94 | ); 95 | 96 | opendap_sync_1bit #( 97 | .N_STAGES (N_SYNC_STAGES) 98 | ) sync_ack ( 99 | .clk (clk_src), 100 | .rst_n (rst_n_src), 101 | .i (dst_ack), 102 | .o (src_ack) 103 | ); 104 | 105 | // ---------------------------------------------------------------------------- 106 | // src state machine 107 | 108 | reg src_waiting_for_downstream; 109 | reg src_pready_r; 110 | 111 | always @ (posedge clk_src or negedge rst_n_src) begin 112 | if (!rst_n_src) begin 113 | src_req <= 1'b0; 114 | src_waiting_for_downstream <= 1'b0; 115 | src_prdata_pslverr <= {W_DATA + 1{1'b0}}; 116 | src_pready_r <= 1'b1; 117 | end else if (src_waiting_for_downstream) begin 118 | if (src_req && src_ack) begin 119 | // Request was acknowledged, so deassert. 120 | src_req <= 1'b0; 121 | end else if (!(src_req || src_ack)) begin 122 | // Downstream transfer has finished, data is valid. 123 | src_pready_r <= 1'b1; 124 | src_waiting_for_downstream <= 1'b0; 125 | // Note this assignment is cross-domain (but data has been stable 126 | // for duration of ack synchronisation delay): 127 | src_prdata_pslverr <= dst_prdata_pslverr; 128 | end 129 | end else begin 130 | // paddr, pwdata and pwrite are all valid during the setup phase, and 131 | // APB defines the setup phase to always last one cycle and proceed 132 | // to access phase. So, we can ignore penable, and pready is ignored. 133 | if (src_psel) begin 134 | src_pready_r <= 1'b0; 135 | src_req <= 1'b1; 136 | src_waiting_for_downstream <= 1'b1; 137 | end 138 | end 139 | end 140 | 141 | // Bus request launch register is not resettable 142 | always @ (posedge clk_src) begin 143 | if (src_psel && !src_waiting_for_downstream) 144 | src_paddr_pwdata_pwrite <= {src_paddr, src_pwdata, src_pwrite}; 145 | end 146 | 147 | assign {src_prdata, src_pslverr} = src_prdata_pslverr; 148 | assign src_pready = src_pready_r; 149 | 150 | // ---------------------------------------------------------------------------- 151 | // dst state machine 152 | 153 | wire dst_bus_finish = dst_penable && dst_pready; 154 | reg dst_psel_r; 155 | reg dst_penable_r; 156 | 157 | always @ (posedge clk_dst or negedge rst_n_dst) begin 158 | if (!rst_n_dst) begin 159 | dst_ack <= 1'b0; 160 | end else if (dst_req) begin 161 | dst_ack <= 1'b1; 162 | end else if (!dst_req && dst_ack && !dst_psel_r) begin 163 | dst_ack <= 1'b0; 164 | end 165 | end 166 | 167 | always @ (posedge clk_dst or negedge rst_n_dst) begin 168 | if (!rst_n_dst) begin 169 | dst_psel_r <= 1'b0; 170 | dst_penable_r <= 1'b0; 171 | end else if (dst_req && !dst_ack) begin 172 | dst_psel_r <= 1'b1; 173 | // Note this assignment is cross-domain. The src register has been 174 | // stable for the duration of the req sync delay. 175 | dst_paddr_pwdata_pwrite <= src_paddr_pwdata_pwrite; 176 | end else if (dst_psel_r && !dst_penable_r) begin 177 | dst_penable_r <= 1'b1; 178 | end else if (dst_bus_finish) begin 179 | dst_psel_r <= 1'b0; 180 | dst_penable_r <= 1'b0; 181 | end 182 | end 183 | 184 | // Bus response launch register is not resettable 185 | always @ (posedge clk_dst) begin 186 | if (dst_bus_finish) 187 | dst_prdata_pslverr <= {dst_prdata, dst_pslverr}; 188 | end 189 | 190 | assign dst_psel = dst_psel_r; 191 | assign dst_penable = dst_penable_r; 192 | assign {dst_paddr, dst_pwdata, dst_pwrite} = dst_paddr_pwdata_pwrite; 193 | 194 | endmodule 195 | 196 | `ifndef YOSYS 197 | `default_nettype wire 198 | `endif 199 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /hdl/opendap_mem_ap_apb.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // APB3 Mem-AP implemementation 7 | 8 | `default_nettype none 9 | 10 | module opendap_mem_ap_apb #( 11 | // Bring your own JEP106 code 12 | parameter [10:0] IDR_DESIGNER = 11'h7ff, 13 | parameter [3:0] IDR_REVISION = 4'h0, 14 | 15 | // Base of debug registers or ROM table 16 | parameter [31:0] BASE = 32'h0000_0000, 17 | 18 | // Minimum of 10 (A[9:0]). 12 is common, for 4kB pages. 19 | parameter TAR_INCREMENT_BITS = 12, 20 | 21 | parameter W_ADDR = 32, // do not modify 22 | parameter W_DATA = 32 // do not modify 23 | ) ( 24 | input wire swclk, 25 | input wire rst_n_por, 26 | 27 | input wire clk_dst, 28 | input wire rst_n_dst, 29 | 30 | // DP-AP bus 31 | input wire [5:0] dpacc_addr, 32 | input wire [W_DATA-1:0] dpacc_wdata, 33 | 34 | input wire dpacc_wen, 35 | input wire dpacc_ren, 36 | input wire dpacc_abort, 37 | 38 | output reg [W_DATA-1:0] dpacc_rdata, 39 | output wire dpacc_rdy, 40 | output wire dpacc_err, 41 | 42 | // Downstream bus 43 | output wire dst_psel, 44 | output wire dst_penable, 45 | output wire dst_pwrite, 46 | output wire [W_ADDR-1:0] dst_paddr, 47 | output wire [W_DATA-1:0] dst_pwdata, 48 | input wire [W_DATA-1:0] dst_prdata, 49 | input wire dst_pready, 50 | input wire dst_pslverr 51 | ); 52 | 53 | // ---------------------------------------------------------------------------- 54 | // AP logic 55 | 56 | // Registers for extensions we don't implement (like T0TR from the tagged 57 | // memory extension), are simply RES0, so not listed explicitly. 58 | 59 | localparam REG_CSW = 6'h00; 60 | localparam REG_TAR = 6'h01; 61 | localparam REG_DRW = 6'h03; 62 | localparam REG_BD0 = 6'h04; 63 | localparam REG_BD1 = 6'h05; 64 | localparam REG_BD2 = 6'h06; 65 | localparam REG_BD3 = 6'h07; 66 | localparam REG_CFG = 6'h3d; 67 | localparam REG_BASE = 6'h3e; 68 | localparam REG_IDR = 6'h3f; 69 | 70 | 71 | reg csw_tr_in_prog; // FIXME drive this 72 | reg csw_addr_inc; 73 | 74 | always @ (posedge swclk or negedge rst_n_por) begin 75 | if (!rst_n_por) begin 76 | csw_addr_inc <= 1'b0; 77 | end else if (dpacc_wen && dpacc_addr == REG_CSW) begin 78 | csw_addr_inc <= dpacc_wdata[4]; 79 | end 80 | end 81 | 82 | reg [31:0] tar; 83 | 84 | always @ (posedge swclk or negedge rst_n_por) begin 85 | if (!rst_n_por) begin 86 | tar <= {W_ADDR{1'b0}}; 87 | end else if (dpacc_wen && dpacc_addr == REG_TAR) begin 88 | tar <= {dpacc_wdata[W_ADDR-1:2], 2'b00}; 89 | end else if ((dpacc_wen || dpacc_ren) && csw_addr_inc && dpacc_addr == REG_DRW) begin 90 | // Note only DRW memory accesses increment, not BDx. 91 | tar <= { 92 | tar[W_ADDR-1:TAR_INCREMENT_BITS], 93 | tar[TAR_INCREMENT_BITS-1:2] + 1'b1, // self-determined size due to concat 94 | 2'b00 95 | }; 96 | end 97 | end 98 | 99 | reg [5:0] dpacc_addr_prev; 100 | always @ (posedge swclk or negedge rst_n_por) begin 101 | if (!rst_n_por) begin 102 | dpacc_addr_prev <= 6'h0; 103 | end else if (dpacc_ren) begin 104 | dpacc_addr_prev <= dpacc_addr; 105 | end 106 | end 107 | 108 | // Register file read mux 109 | 110 | wire [W_DATA-1:0] bridge_prdata; 111 | 112 | always @ (*) begin 113 | case (dpacc_addr_prev) 114 | 115 | REG_CSW: dpacc_rdata = { 116 | 1'b0, // DbgSwEnable, unimplemented 117 | 7'h0, // Prot, unused for APB3 hence unimplemented 118 | 1'b0, // SDeviceEn, unimplemented 119 | 7'h0, // RES0 120 | 4'h0, // Type, unimplemented 121 | 4'h0, // Mode=Basic (no barriers), RO as we only have one mode 122 | csw_tr_in_prog, 123 | 1'b1, // DeviceEn=1 always 124 | 1'b0, // MSB of AddrInc is tied low as APB3 AP doesn't support packed transfers 125 | csw_addr_inc, 126 | 1'b0, // RES0 127 | 3'h2 // Size=2, only 32-bit transfers supported 128 | }; 129 | 130 | REG_TAR: dpacc_rdata = tar; 131 | 132 | REG_DRW: dpacc_rdata = bridge_prdata; 133 | 134 | REG_BD0: dpacc_rdata = bridge_prdata; 135 | 136 | REG_BD1: dpacc_rdata = bridge_prdata; 137 | 138 | REG_BD2: dpacc_rdata = bridge_prdata; 139 | 140 | REG_BD3: dpacc_rdata = bridge_prdata; 141 | 142 | REG_CFG: dpacc_rdata = { 143 | 29'h0, // RES0 144 | 1'b0, // LD=0, no large data 145 | 1'b0, // LA=0, no long address (why are addresses long and data large) 146 | 1'b0 // BE=0, we support only the *correct* endianness 147 | }; 148 | 149 | REG_BASE: dpacc_rdata = BASE; 150 | 151 | REG_IDR: dpacc_rdata = { 152 | IDR_REVISION, 153 | IDR_DESIGNER, 154 | 4'h8, // CLASS = Mem-AP 155 | 5'h0, // RES0 156 | 4'h0, // VARIANT = 0 157 | 4'h2 // TYPE = APB2/APB3 158 | }; 159 | 160 | default: dpacc_rdata = 32'h0; 161 | 162 | endcase 163 | end 164 | 165 | // ---------------------------------------------------------------------------- 166 | // Non-optional async bridge 167 | // (clock crossing and downstream protocol handling) 168 | 169 | // Taking advantage of the bridge starting transfers immediately off of the 170 | // setup phase, and not requiring a valid access phase. 171 | wire start_transfer; 172 | wire bridge_psel; 173 | wire bridge_penable = 1'b0; 174 | 175 | wire bridge_pwrite = dpacc_wen; 176 | wire [W_ADDR-1:0] bridge_paddr; 177 | wire [W_DATA-1:0] bridge_pwdata = dpacc_wdata; 178 | wire bridge_pready; 179 | wire bridge_pslverr; 180 | 181 | opendap_apb_async_bridge #( 182 | .W_ADDR (W_ADDR), 183 | .W_DATA (W_DATA), 184 | .N_SYNC_STAGES (2) 185 | ) async_bridge ( 186 | .clk_src (swclk), 187 | .rst_n_src (rst_n_por), 188 | 189 | .clk_dst (clk_dst), 190 | .rst_n_dst (rst_n_dst), 191 | 192 | .src_psel (bridge_psel), 193 | .src_penable (bridge_penable), 194 | .src_pwrite (bridge_pwrite), 195 | .src_paddr (bridge_paddr), 196 | .src_pwdata (bridge_pwdata), 197 | .src_prdata (bridge_prdata), 198 | .src_pready (bridge_pready), 199 | .src_pslverr (bridge_pslverr), 200 | 201 | .dst_psel (dst_psel), 202 | .dst_penable (dst_penable), 203 | .dst_pwrite (dst_pwrite), 204 | .dst_paddr (dst_paddr), 205 | .dst_pwdata (dst_pwdata), 206 | .dst_prdata (dst_prdata), 207 | .dst_pready (dst_pready), 208 | .dst_pslverr (dst_pslverr) 209 | ); 210 | 211 | // Send bus transfers to bridge 212 | 213 | assign bridge_paddr = { 214 | tar[W_ADDR-1:4], 215 | dpacc_addr == REG_DRW ? tar[3:2] : dpacc_addr[1:0], 216 | 2'b00 217 | }; 218 | 219 | wire dpacc_is_mem = dpacc_addr == REG_DRW || (dpacc_addr & 6'h3c) == REG_BD0; 220 | assign bridge_psel = (dpacc_wen || dpacc_ren) && dpacc_is_mem; 221 | 222 | // TODO abort 223 | assign dpacc_rdy = bridge_pready; 224 | 225 | reg error_vld; 226 | 227 | always @ (posedge swclk or negedge rst_n_por) begin 228 | if (!rst_n_por) begin 229 | error_vld <= 1'b0; 230 | end else begin 231 | error_vld <= bridge_psel || !bridge_pready; 232 | end 233 | end 234 | 235 | 236 | assign dpacc_err = bridge_pslverr && error_vld; 237 | 238 | endmodule 239 | 240 | `ifndef YOSYS 241 | `default_nettype wire 242 | `endif 243 | -------------------------------------------------------------------------------- /hdl/opendap_sw_dp.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // SW-DP implementation. Supports DPv2, SWD version 2. 7 | 8 | `default_nettype none 9 | 10 | module opendap_sw_dp #( 11 | // This is a MINDP implementation, so DPIDR[16] *must* be set. (Also I 12 | // don't have a JEP106 ID for the DPIDR -- you'll have to bring your own) 13 | parameter DPIDR = 32'hdeadbeef, 14 | parameter TARGETID = 32'hbaadf00d 15 | ) ( 16 | input wire swclk, 17 | input wire rst_n, 18 | 19 | input wire swdi, 20 | output reg swdo, 21 | output reg swdo_en, 22 | 23 | output wire cdbgpwrupreq, 24 | input wire cdbgpwrupack, 25 | output wire csyspwrupreq, 26 | input wire csyspwrupack, 27 | output wire cdbgrstreq, 28 | input wire cdbgrstack, 29 | 30 | input wire [3:0] instid, 31 | input wire eventstat, 32 | 33 | // AP Interface: 34 | // - sel, addr and wdata are valid only on the cycle where wen/ren is asserted. 35 | // - AP may then take rdy low for arbitrarily many cycles. 36 | // - rdata must be valid from the first cycle rdy goes high after a ren, 37 | // until the next time ren/wen is asserted. 38 | // - err may go high only on the first cycle rdy is high after a ren/wen. 39 | // - when abort is asserted, rdy must go high on the next cycle. 40 | output wire [7:0] ap_sel, 41 | output wire [5:0] ap_addr, 42 | output wire [31:0] ap_wdata, 43 | 44 | output wire ap_wen, 45 | output wire ap_ren, 46 | output wire ap_abort, 47 | 48 | input wire [31:0] ap_rdata, 49 | input wire ap_rdy, 50 | input wire ap_err 51 | ); 52 | 53 | wire [1:0] hostacc_addr; 54 | wire hostacc_r_nw; 55 | wire hostacc_ap_ndp; 56 | wire [31:0] hostacc_wdata; 57 | wire hostacc_en; 58 | reg [31:0] hostacc_rdata; 59 | wire hostacc_fault; 60 | wire hostacc_wait; 61 | 62 | wire hostacc_write = hostacc_en && !hostacc_fault && !hostacc_r_nw; 63 | wire hostacc_read = hostacc_en && !hostacc_fault && hostacc_r_nw; 64 | 65 | wire set_wdataerr; 66 | wire set_stickyorun; 67 | wire set_stickyerr = ap_rdy && ap_err; 68 | wire set_readok = hostacc_read && (hostacc_ap_ndp || hostacc_addr == 2'b11); 69 | wire clear_readok; // driven by serial unit 70 | 71 | // ---------------------------------------------------------------------------- 72 | // DP register file 73 | 74 | reg [7:0] select_apsel; 75 | reg [3:0] select_apbanksel; 76 | reg [3:0] select_dpbanksel; 77 | 78 | always @ (posedge swclk or negedge rst_n) begin 79 | if (!rst_n) begin 80 | select_apsel <= 8'h0; 81 | select_apbanksel <= 4'h0; 82 | select_dpbanksel <= 4'h0; 83 | end else if (hostacc_write && !hostacc_ap_ndp && hostacc_addr == 2'b10) begin 84 | select_apsel <= hostacc_wdata[31:24]; 85 | select_apbanksel <= hostacc_wdata[7:4]; 86 | select_dpbanksel <= hostacc_wdata[3:0]; 87 | end 88 | end 89 | 90 | reg ctrl_stat_csyspwrupreq; 91 | reg ctrl_stat_cdbgpwrupreq; 92 | reg ctrl_stat_cdbgrstreq; 93 | reg ctrl_stat_orundetect; 94 | reg ctrl_stat_readok; 95 | reg ctrl_stat_stickyerr; 96 | reg ctrl_stat_stickyorun; 97 | reg ctrl_stat_wdataerr; 98 | 99 | assign csyspwrupreq = ctrl_stat_csyspwrupreq; 100 | assign cdbgpwrupreq = ctrl_stat_cdbgpwrupreq; 101 | assign cdbgrstreq = ctrl_stat_cdbgrstreq; 102 | 103 | always @ (posedge swclk or negedge rst_n) begin 104 | if (!rst_n) begin 105 | ctrl_stat_cdbgpwrupreq <= 1'b0; 106 | ctrl_stat_csyspwrupreq <= 1'b0; 107 | ctrl_stat_orundetect <= 1'b0; 108 | ctrl_stat_readok <= 1'b0; 109 | ctrl_stat_stickyerr <= 1'b0; 110 | ctrl_stat_stickyorun <= 1'b0; 111 | ctrl_stat_wdataerr <= 1'b0; 112 | end else begin 113 | if (hostacc_write && !hostacc_ap_ndp && hostacc_addr == 2'b00) begin 114 | // ABORT write 115 | ctrl_stat_stickyorun <= ctrl_stat_stickyorun && !hostacc_wdata[4]; 116 | ctrl_stat_wdataerr <= ctrl_stat_wdataerr && !hostacc_wdata[3]; 117 | ctrl_stat_stickyerr <= ctrl_stat_stickyerr && !hostacc_wdata[2]; 118 | end 119 | if (hostacc_write && !hostacc_ap_ndp && hostacc_addr == 2'b01 && select_dpbanksel == 4'h0) begin 120 | // CTRL/STAT write 121 | ctrl_stat_csyspwrupreq <= hostacc_wdata[30]; 122 | ctrl_stat_cdbgpwrupreq <= hostacc_wdata[28]; 123 | ctrl_stat_cdbgrstreq <= hostacc_wdata[26]; 124 | // We are MINDP; implement TRNCNT/MASKLANE/TRNMODE as RAZ/WI. 125 | ctrl_stat_orundetect <= hostacc_wdata[0]; 126 | // B1.2 says STICKYORUN becomes UNKNOWN if ORUNDETECT is cleared when 127 | // STICKYORUN is left set. However it seems polite to just clear it. 128 | ctrl_stat_stickyorun <= ctrl_stat_stickyorun && hostacc_wdata[0]; 129 | end 130 | if (set_wdataerr) 131 | ctrl_stat_wdataerr <= 1'b1; 132 | if (set_stickyorun) 133 | ctrl_stat_stickyorun <= 1'b1; 134 | if (set_stickyerr) 135 | ctrl_stat_stickyerr <= 1'b1; 136 | ctrl_stat_readok <= (ctrl_stat_readok || set_readok) && !clear_readok; 137 | end 138 | end 139 | 140 | always @ (*) begin 141 | if (hostacc_ap_ndp) begin 142 | hostacc_rdata = ap_rdata; 143 | end else casez ({hostacc_addr, select_dpbanksel}) 144 | 6'h0z: hostacc_rdata = DPIDR; 145 | 146 | 6'h10: hostacc_rdata = { 147 | csyspwrupack, 148 | ctrl_stat_csyspwrupreq, 149 | cdbgpwrupack, 150 | ctrl_stat_cdbgpwrupreq, 151 | cdbgrstack, 152 | ctrl_stat_cdbgrstreq, 153 | 2'b00, // RES0 154 | 12'h0, // TRNCOUNT 155 | 4'h0, // MASKLANE 156 | ctrl_stat_wdataerr, 157 | ctrl_stat_readok, 158 | ctrl_stat_stickyerr, 159 | 1'b0, // STICKYCMP 160 | 2'b00, // TRNMODE 161 | ctrl_stat_stickyorun, 162 | ctrl_stat_orundetect 163 | }; 164 | 165 | 6'h11: hostacc_rdata = 32'h0000_0040; // DLCR 166 | 167 | 6'h12: hostacc_rdata = TARGETID; 168 | 169 | 6'h13: hostacc_rdata = { // DLPIDR 170 | instid, // TINSTANCE 171 | 24'h0, // RES0 172 | 4'h1 // PROTVSN 173 | }; 174 | 175 | 6'h14: hostacc_rdata = { // EVENTSTAT 176 | 31'h0, // RES0 177 | eventstat 178 | }; 179 | 180 | 6'h1z: hostacc_rdata = 32'h0000_0000; // RES0 181 | 182 | 6'h2z: hostacc_rdata = 32'h0000_0000; // RESEND is handled inside the serial comms. 183 | 184 | 6'h3z: hostacc_rdata = ap_rdata; // RDBUFF 185 | endcase 186 | end 187 | 188 | reg resend_possible; 189 | 190 | always @ (posedge swclk or negedge rst_n) begin 191 | if (!rst_n) begin 192 | resend_possible <= 1'b0; 193 | end else if (hostacc_en) begin 194 | // B2.2.8 says RESEND returns specifically the last AP read or RDBUFF 195 | // read, not just the last read in general. 196 | // 197 | // Since we are just recirculating the shift register, we need to fail 198 | // a RESEND if any *other* type of read takes place since the last 199 | // AP/RDBUFF read, as well as if any write takes place. Otherwise we 200 | // would give the wrong data! 201 | // 202 | // (RESEND is the only other exception -- we can repeatedly send the 203 | // same data as many times as the host likes.) 204 | resend_possible <= hostacc_read && ( 205 | hostacc_ap_ndp || // AP read 206 | hostacc_addr == 2'b11 || // RDBUFF 207 | hostacc_addr == 2'b10 // RESEND 208 | ); 209 | end 210 | end 211 | 212 | wire hostacc_protocol_err = 213 | (hostacc_write && !hostacc_ap_ndp && {hostacc_addr, select_dpbanksel} == 6'h11 && // Bad TURNROUND 214 | hostacc_wdata[9:8] != 2'b00) || 215 | (hostacc_read && !hostacc_ap_ndp && hostacc_addr == 2'b10 && // Bad RESEND 216 | !resend_possible); 217 | 218 | // ---------------------------------------------------------------------------- 219 | // Serial comms unit 220 | 221 | // See B4.2.3, B4.2.4 for this list (same list of exceptions for both WAIT and 222 | // FAULT). See also B4.2.8 for a summary of all target responses. 223 | 224 | // Bit of a hole in the spec here as you may need to write SELECT to select 225 | // CTRL/STAT in the first place, so if DPBANKSEL is currently nonzero, it's 226 | // impossible to read CTRL/STAT and determine which flag is causing your 227 | // FAULT. Fairly minor issue as the nonzero DPBANKSEL registers are much less 228 | // used than CTRL/STAT. 229 | 230 | wire access_always_ok = !hostacc_ap_ndp && ( 231 | hostacc_r_nw && hostacc_addr == 2'b00 || // DPIDR read 232 | !hostacc_r_nw && hostacc_addr == 2'b00 || // ABORT write 233 | hostacc_r_nw && hostacc_addr == 2'b01 && select_dpbanksel == 4'h0 // C/S read 234 | ); 235 | 236 | // Most accesses when a sticky flag is set cause a FAULT response. Same is 237 | // true for WAIT responses when an AP transfer is in flight. The exception is 238 | // those accesses which are necessary for diagnosing and clearing the fault 239 | // condition. 240 | 241 | wire any_sticky_errors = ctrl_stat_stickyorun || ctrl_stat_stickyerr || ctrl_stat_wdataerr; 242 | 243 | // We are decoding this straight out of the serial comms' shift register, so 244 | // must be combinatorial, and not a function of hostacc_en. 245 | 246 | assign hostacc_fault = any_sticky_errors && !access_always_ok; 247 | 248 | assign hostacc_wait = !(ap_rdy || access_always_ok); 249 | 250 | opendap_sw_dp_serial_comms serial_comms ( 251 | .swclk (swclk), 252 | .rst_n (rst_n), 253 | 254 | .swdi (swdi), 255 | .swdo (swdo), 256 | .swdo_en (swdo_en), 257 | 258 | .bus_addr (hostacc_addr), 259 | .bus_r_nw (hostacc_r_nw), 260 | .bus_ap_ndp (hostacc_ap_ndp), 261 | .bus_wdata (hostacc_wdata), 262 | .bus_rdata (hostacc_rdata), 263 | .bus_en (hostacc_en), 264 | 265 | .targetsel_expected ({instid, TARGETID[27:0]}), 266 | .dp_set_wdataerr (set_wdataerr), 267 | .dp_set_stickyorun (set_stickyorun), 268 | .dp_clear_readok (clear_readok), 269 | .dp_orundetect (ctrl_stat_orundetect), 270 | .dp_acc_fault (hostacc_fault), 271 | .dp_acc_protocol_err (hostacc_protocol_err), 272 | .dp_acc_wait (hostacc_wait) 273 | ); 274 | 275 | // ---------------------------------------------------------------------------- 276 | // AP signalling 277 | 278 | assign ap_wen = hostacc_write && hostacc_ap_ndp; 279 | assign ap_ren = hostacc_read && hostacc_ap_ndp; 280 | assign ap_sel = select_apsel; 281 | assign ap_addr = {select_apbanksel, hostacc_addr}; 282 | assign ap_wdata = hostacc_wdata; 283 | // DAPABORT is lsb of the ABORT register. When we assert this flag, rdy must 284 | // be asserted high on the next cycle. 285 | assign ap_abort = hostacc_write && !hostacc_ap_ndp && hostacc_addr == 2'b00 && hostacc_wdata[0]; 286 | 287 | endmodule 288 | 289 | `ifndef YOSYS 290 | `default_nettype wire 291 | `endif 292 | -------------------------------------------------------------------------------- /hdl/opendap_sw_dp_serial_comms.v: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Part of the OpenDAP project. Original author: Luke Wren 3 | // SPDX-License-Identifier CC0-1.0 4 | // ---------------------------------------------------------------------------- 5 | 6 | // Serialise and deserialise data. Track link state, detect parity or serial 7 | // protocol errors, provide OK/WAIT/FAULT responses. Perform parallel 8 | // accesses on the core DP logic, which may be forwarded on to the AP. 9 | 10 | `default_nettype none 11 | 12 | module opendap_sw_dp_serial_comms ( 13 | input wire swclk, 14 | input wire rst_n, 15 | 16 | input wire swdi, 17 | output reg swdo, 18 | output reg swdo_en, 19 | 20 | output wire [1:0] bus_addr, 21 | output wire bus_r_nw, 22 | output wire bus_ap_ndp, 23 | output wire [31:0] bus_wdata, 24 | output reg bus_en, 25 | input wire [31:0] bus_rdata, 26 | 27 | input wire [31:0] targetsel_expected, 28 | 29 | output reg dp_set_wdataerr, 30 | output reg dp_set_stickyorun, 31 | output reg dp_clear_readok, 32 | input wire dp_orundetect, 33 | input wire dp_acc_fault, 34 | input wire dp_acc_protocol_err, 35 | input wire dp_acc_wait 36 | ); 37 | 38 | // ---------------------------------------------------------------------------- 39 | // Single point of registration for SWDIO pad signals 40 | 41 | // There is no use of the unregistered signals inside the DP. Note this module 42 | // is in the cells/ directory, and you are expected to update it with 43 | // appropriate cells for your FPGA/process, e.g. IO cell registers on FPGA. 44 | 45 | wire swdi_reg; 46 | reg swdo_nxt; 47 | reg swdo_en_nxt; 48 | 49 | opendap_swdio_flops io_flops ( 50 | .clk (swclk), 51 | .rst_n (rst_n), 52 | 53 | .dp_swdo_next (swdo_nxt), 54 | .dp_swdo_en_next (swdo_en_nxt), 55 | .dp_swdi_prev (swdi_reg), 56 | 57 | .pad_swdo (swdo), 58 | .pad_swdo_en (swdo_en), 59 | .pad_swdi (swdi) 60 | ); 61 | 62 | // ---------------------------------------------------------------------------- 63 | // Dormant monitor -- watch for transitions between Dormant and SWD line states 64 | 65 | // This is a separate state machine because e.g. line resets can be embedded 66 | // in what seem initially to be well-formed SWD transfers. 67 | 68 | wire exit_dormant; 69 | wire enter_dormant; 70 | wire line_reset; 71 | 72 | opendap_swd_dormant_monitor dormant_monitor ( 73 | .swclk (swclk), 74 | .rst_n (rst_n), 75 | 76 | .swdi_reg (swdi_reg), 77 | .exit_dormant (exit_dormant), 78 | .enter_dormant (enter_dormant), 79 | .line_reset (line_reset) 80 | ); 81 | 82 | // ---------------------------------------------------------------------------- 83 | // Main DP serial comms state machine 84 | 85 | reg [5:0] header_sreg; 86 | reg header_sreg_en; 87 | 88 | reg [31:0] data_sreg; 89 | reg data_sreg_en; 90 | 91 | wire header_ok = ~^header_sreg[4:0] && !header_sreg[5] && swdi_reg; 92 | wire [1:0] header_addr = header_sreg[3:2]; 93 | wire header_r_nw = header_sreg[1]; 94 | wire header_ap_ndp = header_sreg[0]; 95 | 96 | wire header_is_dpidr_read = !header_ap_ndp && header_r_nw && header_addr == 2'b00; 97 | wire header_is_targetsel_write = !header_ap_ndp && !header_r_nw && header_addr == 2'b11; 98 | wire header_is_resend = !header_ap_ndp && header_r_nw && header_addr == 2'b10; 99 | wire header_modifies_readok = header_r_nw && (header_ap_ndp || header_addr == 2'b11); 100 | 101 | assign bus_ap_ndp = header_ap_ndp; 102 | assign bus_r_nw = header_r_nw; 103 | assign bus_addr = header_addr; 104 | assign bus_wdata = data_sreg; 105 | 106 | localparam W_LINK_STATE = 2; 107 | localparam LINK_DORMANT = 2'd0; 108 | localparam LINK_RESET = 2'd1; 109 | localparam LINK_ACTIVE = 2'd2; 110 | localparam LINK_LOCKEDOUT = 2'd3; 111 | 112 | localparam W_PHASE_STATE = 4; 113 | localparam PHASE_IDLE = 4'd0; 114 | localparam PHASE_HEADER = 4'd1; 115 | localparam PHASE_PARK = 4'd2; 116 | localparam PHASE_ACK_WAIT = 4'd3; 117 | localparam PHASE_ACK_FAULT = 4'd4; 118 | localparam PHASE_ACK_OK = 4'd5; 119 | localparam PHASE_TURN_TO_IDLE = 4'd6; 120 | localparam PHASE_TURN_TO_WDATA = 4'd7; 121 | localparam PHASE_WDATA = 4'd8; 122 | localparam PHASE_RDATA = 4'd9; 123 | localparam PHASE_TURN_FROM_RDATA = 4'd10; 124 | localparam PHASE_TARGETSEL_ACK = 4'd11; 125 | localparam PHASE_TARGETSEL_DATA = 4'd12; 126 | 127 | reg [W_LINK_STATE-1:0] link_state, link_state_nxt; 128 | reg [W_PHASE_STATE-1:0] phase, phase_nxt; 129 | reg [5:0] bit_ctr, bit_ctr_nxt; 130 | reg data_parity, data_parity_nxt; 131 | 132 | always @ (*) begin 133 | link_state_nxt = link_state; 134 | phase_nxt = phase; 135 | bit_ctr_nxt = bit_ctr - |bit_ctr; 136 | data_parity_nxt = 1'b0; 137 | 138 | header_sreg_en = 1'b0; 139 | data_sreg_en = 1'b0; 140 | 141 | bus_en = 1'b0; 142 | dp_set_wdataerr = 1'b0; 143 | dp_set_stickyorun = 1'b0; 144 | dp_clear_readok = 1'b0; 145 | 146 | swdo_nxt = 1'b0; 147 | swdo_en_nxt = 1'b0; 148 | 149 | if (link_state == LINK_RESET || link_state == LINK_ACTIVE) case (phase) 150 | 151 | PHASE_IDLE: begin 152 | if (swdi_reg) begin 153 | phase_nxt = PHASE_HEADER; 154 | bit_ctr_nxt = 6'd5; 155 | end 156 | end 157 | 158 | PHASE_HEADER: begin 159 | header_sreg_en = 1'b1; 160 | if (~|bit_ctr) begin 161 | phase_nxt = PHASE_PARK; 162 | end 163 | end 164 | 165 | PHASE_PARK: begin 166 | // This is the cycle where the park bit appears in swdi_reg. That means, in the outside 167 | // world, the park-to-ACK turnaround is ongoing. The posedge after we register the park 168 | // bit is the edge where we assert the first ACK bit. 169 | if (!header_ok) begin 170 | link_state_nxt = LINK_LOCKEDOUT; 171 | dp_set_stickyorun = dp_orundetect; 172 | end else if (header_is_targetsel_write) begin 173 | if (link_state == LINK_RESET) begin 174 | // Do not drive swdo_en -- TARGETSEL is supposed to be unacknowledged. 175 | phase_nxt = PHASE_TARGETSEL_ACK; 176 | bit_ctr_nxt = 6'd4; 177 | end else begin 178 | // Writes to TARGETSEL which don't immediately follow a line reset are 179 | // UNPREDICTABLE (ref B4.3.4). We allow multiple successful TARGETSELs when in 180 | // the reset state, but after leaving the reset state via DPIDR read, TARGETSEL 181 | // causes unconditional lockout. 182 | link_state_nxt = LINK_LOCKEDOUT; 183 | end 184 | end else if (link_state == LINK_RESET && !header_is_dpidr_read) begin 185 | // In reset state, anything but TARGETSEL write or DPIDR read causes immediate lockout 186 | link_state_nxt = LINK_LOCKEDOUT; 187 | end else begin 188 | // If we're in reset this must be DPIDR read, so go to active. 189 | link_state_nxt = LINK_ACTIVE; 190 | swdo_en_nxt = 1'b1; 191 | bit_ctr_nxt = 6'd2; 192 | if (dp_acc_fault) begin 193 | // DP signals fault on AP access with any sticky flag set. 194 | phase_nxt = PHASE_ACK_FAULT; 195 | dp_set_stickyorun = dp_orundetect; 196 | dp_clear_readok = header_modifies_readok; 197 | end else if (dp_acc_wait) begin 198 | phase_nxt = PHASE_ACK_WAIT; 199 | dp_set_stickyorun = dp_orundetect; 200 | dp_clear_readok = header_modifies_readok; 201 | end else begin 202 | bus_en = header_r_nw; 203 | if (dp_acc_protocol_err) begin 204 | link_state_nxt = LINK_LOCKEDOUT; 205 | swdo_en_nxt = 1'b0; 206 | end else begin 207 | phase_nxt = PHASE_ACK_OK; 208 | swdo_nxt = 1'b1; 209 | end 210 | end 211 | end 212 | end 213 | 214 | PHASE_ACK_FAULT: begin 215 | if (|bit_ctr) begin 216 | swdo_en_nxt = 1'b1; 217 | swdo_nxt = bit_ctr == 6'd1; 218 | end else begin 219 | phase_nxt = PHASE_TURN_TO_IDLE; 220 | // At minimum, one cycle turnaround, plus another cycle to make up for the cycle we 221 | // swallowed by jumping straight in form the Park cycle to get the ACK out early 222 | // enough. If ORUNDETECT is set, an additional 33 cycles (data + parity). 223 | if (dp_orundetect) 224 | bit_ctr_nxt = 6'd34; 225 | else 226 | bit_ctr_nxt = 6'd1; 227 | end 228 | end 229 | 230 | PHASE_ACK_WAIT: begin 231 | if (|bit_ctr) begin 232 | swdo_en_nxt = 1'b1; 233 | swdo_nxt = bit_ctr == 6'd2; 234 | end else begin 235 | phase_nxt = PHASE_TURN_TO_IDLE; 236 | if (dp_orundetect) 237 | bit_ctr_nxt = 6'd34; 238 | else 239 | bit_ctr_nxt = 6'd1; 240 | end 241 | end 242 | 243 | PHASE_TURN_TO_IDLE: begin 244 | if (~|bit_ctr) 245 | phase_nxt = PHASE_IDLE; 246 | end 247 | 248 | PHASE_ACK_OK: begin 249 | if (|bit_ctr) begin 250 | swdo_en_nxt = 1'b1; 251 | end else if (header_r_nw) begin 252 | phase_nxt = PHASE_RDATA; 253 | swdo_en_nxt = 1'b1; 254 | swdo_nxt = data_sreg[0]; 255 | data_parity_nxt = data_parity ^ data_sreg[0]; 256 | data_sreg_en = 1'b1; 257 | bit_ctr_nxt = 6'd32; 258 | end else begin 259 | phase_nxt = PHASE_TURN_TO_WDATA; 260 | bit_ctr_nxt = 1'b1; 261 | end 262 | end 263 | 264 | PHASE_RDATA: begin 265 | if (bit_ctr == 6'd0) begin 266 | phase_nxt = PHASE_TURN_FROM_RDATA; 267 | bit_ctr_nxt = 6'd1; 268 | end else if (bit_ctr == 6'd1) begin 269 | swdo_en_nxt = 1'b1; 270 | swdo_nxt = data_parity; 271 | end else begin 272 | swdo_en_nxt = 1'b1; 273 | swdo_nxt = data_sreg[0]; 274 | data_parity_nxt = data_parity ^ data_sreg[0]; 275 | data_sreg_en = 1'b1; 276 | end 277 | end 278 | 279 | PHASE_TURN_FROM_RDATA: begin 280 | // This lasts for two cycles, for similar reasons to TURN_TO_WDATA 281 | if (~|bit_ctr) 282 | phase_nxt = PHASE_IDLE; 283 | end 284 | 285 | PHASE_TURN_TO_WDATA: begin 286 | // This lasts for two cycles -- we swallowed one cycle by jumping the gun at the park 287 | // bit, to get the ACK out on time. Now we need to sync back up to the bits on swdi_reg. 288 | if (~|bit_ctr) begin 289 | phase_nxt = PHASE_WDATA; 290 | bit_ctr_nxt = 6'd32; 291 | end 292 | end 293 | 294 | PHASE_WDATA: begin 295 | if (~|bit_ctr) begin 296 | phase_nxt = PHASE_IDLE; 297 | if (swdi_reg != data_parity) begin 298 | dp_set_wdataerr = 1'b1; 299 | end else begin 300 | bus_en = 1'b1; 301 | if (dp_acc_protocol_err) begin 302 | link_state_nxt = LINK_LOCKEDOUT; 303 | end 304 | end 305 | end else begin 306 | data_sreg_en = 1'b1; 307 | data_parity_nxt = data_parity ^ swdi_reg; 308 | end 309 | end 310 | 311 | PHASE_TARGETSEL_ACK: begin 312 | if (~|bit_ctr) begin 313 | phase_nxt = PHASE_TARGETSEL_DATA; 314 | bit_ctr_nxt = 6'd32; 315 | end 316 | 317 | end 318 | 319 | PHASE_TARGETSEL_DATA: begin 320 | // There are lots of words in the spec but TARGETSEL seems to really be "go to lockout 321 | // state if ID doesn't match". A parity error is treated as a ID mismatch (B4.1.6) and 322 | // also, confusingly, as a protocol error (B4.3.4), which we would handle identically. 323 | if (~|bit_ctr) begin 324 | if (targetsel_expected != data_sreg || swdi_reg != data_parity) begin 325 | link_state_nxt = LINK_LOCKEDOUT; 326 | end else begin 327 | phase_nxt = PHASE_IDLE; 328 | end 329 | end else begin 330 | data_sreg_en = 1'b1; 331 | data_parity_nxt = data_parity ^ swdi_reg; 332 | end 333 | 334 | end 335 | 336 | endcase 337 | 338 | if (link_state == LINK_DORMANT && exit_dormant) 339 | link_state_nxt = LINK_RESET; 340 | if (link_state == LINK_LOCKEDOUT && line_reset) begin 341 | link_state_nxt = LINK_RESET; 342 | dp_set_stickyorun = dp_orundetect; 343 | end 344 | if (enter_dormant) 345 | link_state_nxt = LINK_DORMANT; 346 | 347 | if (link_state != LINK_RESET && link_state != LINK_ACTIVE) 348 | phase_nxt = PHASE_IDLE; 349 | 350 | end 351 | 352 | always @ (posedge swclk or negedge rst_n) begin 353 | if (!rst_n) begin 354 | link_state <= LINK_DORMANT; 355 | phase <= PHASE_IDLE; 356 | bit_ctr <= 6'h0; 357 | data_parity <= 1'b0; 358 | swdo <= 1'b0; 359 | swdo_en <= 1'b0; 360 | end else begin 361 | link_state <= link_state_nxt; 362 | phase <= phase_nxt; 363 | bit_ctr <= bit_ctr_nxt; 364 | data_parity <= data_parity_nxt; 365 | swdo <= swdo_nxt; 366 | swdo_en <= swdo_en_nxt; 367 | end 368 | end 369 | 370 | always @ (posedge swclk or negedge rst_n) begin 371 | if (!rst_n) begin 372 | data_sreg <= 32'h0; 373 | header_sreg <= 6'h0; 374 | end else begin 375 | // On reads we recirculate the data so that we can RESEND it. 376 | if (data_sreg_en) 377 | data_sreg <= {header_r_nw ? data_sreg[0] : swdi_reg, data_sreg[31:1]}; 378 | if (bus_en && bus_r_nw && !header_is_resend) 379 | data_sreg <= bus_rdata; 380 | 381 | if (header_sreg_en) 382 | header_sreg <= {swdi_reg, header_sreg[5:1]}; 383 | end 384 | end 385 | 386 | endmodule 387 | 388 | `ifndef YOSYS 389 | `default_nettype wire 390 | `endif 391 | --------------------------------------------------------------------------------