external test
diff --git a/verilog/dv/scan_controller_ext/Makefile b/verilog/dv/scan_controller_ext/Makefile
new file mode 100644
index 0000000..eb95226
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/Makefile
@@ -0,0 +1,31 @@
+PWDD := $(shell pwd)
+BLOCKS := $(shell basename $(PWDD))
+
+# ---- Include Partitioned Makefiles ----
+
+CONFIG = caravel_user_project
+
+export COCOTB_REDUCED_LOG_FMT=1
+# Change this line if you want to use existing cocotb test modules:
+#export PYTHONPATH := $(DESIGNS)/verilog/rtl/<your design python tests>
+export LIBPYTHON_LOC=$(shell cocotb-config --libpython)
+
+
+include $(MCW_ROOT)/verilog/dv/make/env.makefile
+include $(MCW_ROOT)/verilog/dv/make/var.makefile
+include $(MCW_ROOT)/verilog/dv/make/cpu.makefile
+include $(MCW_ROOT)/verilog/dv/make/sim.makefile
+
+# change the project.hex to your projects firmware file
+coco_test: scan_controller.hex
+	rm -rf sim_build/
+	mkdir sim_build/
+
+    # change project_tb.v to match your testbench name
+	iverilog -Ttyp -DFUNCTIONAL -DSIM -DUSE_POWER_PINS -DUNIT_DELAY=#1 \
+	-f$(VERILOG_PATH)/includes/includes.rtl.caravel \
+	-f$(USER_PROJECT_VERILOG)/includes/includes.rtl.$(CONFIG) -o sim_build/sim.vvp scan_controller_tb.v
+
+    # change this line to choose the comma separated test cases and the name of your python test module
+	TESTCASE=test_start MODULE=test_scan_controller vvp -M $$(cocotb-config --prefix)/cocotb/libs -m libcocotbvpi_icarus sim_build/sim.vvp
+	! grep failure results.xml
diff --git a/verilog/dv/scan_controller_ext/README.md b/verilog/dv/scan_controller_ext/README.md
new file mode 100644
index 0000000..aa489e9
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/README.md
@@ -0,0 +1,7 @@
+# external controller test
+
+run
+
+    ./configure.py --update-caravel --limit 1
+
+before starting test
diff --git a/verilog/dv/scan_controller_ext/scan_controller.c b/verilog/dv/scan_controller_ext/scan_controller.c
new file mode 100644
index 0000000..6edea7e
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/scan_controller.c
@@ -0,0 +1,74 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Efabless Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <defs.h>
+#include <stub.c>
+
+#define SET(PIN,N) (PIN |=  (1<<N))
+#define CLR(PIN,N) (PIN &= ~(1<<N))
+#define GET(PIN,N) (PIN &   (1<<N))
+
+#define FW_READY    12
+
+void main()
+{
+	/* 
+	IO Control Registers
+	| DM     | VTRIP | SLOW  | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN |
+	| 3-bits | 1-bit | 1-bit | 1-bit  | 1-bit  | 1-bit | 1-bit   | 1-bit   | 1-bit | 1-bit | 1-bit   |
+
+	Output: 0000_0110_0000_1110  (0x1808) = GPIO_MODE_USER_STD_OUTPUT
+	| DM     | VTRIP | SLOW  | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN |
+	| 110    | 0     | 0     | 0      | 0      | 0     | 0       | 1       | 0     | 0     | 0       |
+	
+	 
+	Input: 0000_0001_0000_1111 (0x0402) = GPIO_MODE_USER_STD_INPUT_NOPULL
+	| DM     | VTRIP | SLOW  | AN_POL | AN_SEL | AN_EN | MOD_SEL | INP_DIS | HOLDH | OEB_N | MGMT_EN |
+	| 001    | 0     | 0     | 0      | 0      | 0     | 0       | 0       | 0     | 1     | 0       |
+
+	*/
+
+    // 2 inputs for enable logic analyser control
+	reg_mprj_io_8 =   GPIO_MODE_USER_STD_INPUT_NOPULL;
+	reg_mprj_io_9 =   GPIO_MODE_USER_STD_INPUT_NOPULL;
+
+    /*
+    inputs                 (io_in[28:21]),
+    outputs                (io_out[36:29]),
+    ext_scan_clk       = inputs[0];
+    ext_scan_data_in   = inputs[1];
+    ext_scan_data_out  = outputs[0]
+    ext_scan_select    = inputs[2];   
+    ext_scan_latch_en  = inputs[3];
+    */
+
+    reg_mprj_io_21 = GPIO_MODE_USER_STD_INPUT_NOPULL; // clk
+    reg_mprj_io_22 = GPIO_MODE_USER_STD_INPUT_NOPULL; // data in
+    reg_mprj_io_23 = GPIO_MODE_USER_STD_INPUT_NOPULL; // scan
+    reg_mprj_io_24 = GPIO_MODE_USER_STD_INPUT_NOPULL; // latch
+
+    reg_mprj_io_29 = GPIO_MODE_USER_STD_OUTPUT; // data out
+
+    // outputs for testbench control
+    reg_mprj_io_12 = GPIO_MODE_MGMT_STD_OUTPUT; // fw ready
+
+    /* Apply configuration */
+    reg_mprj_xfer = 1;
+    while (reg_mprj_xfer == 1);
+
+	reg_mprj_datal |= 1 << FW_READY;
+}
diff --git a/verilog/dv/scan_controller_ext/scan_controller.hex b/verilog/dv/scan_controller_ext/scan_controller.hex
new file mode 100755
index 0000000..4dd92e8
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/scan_controller.hex
@@ -0,0 +1,59 @@
+@00000000

+6F 00 00 0B 13 00 00 00 13 00 00 00 13 00 00 00 

+13 00 00 00 13 00 00 00 13 00 00 00 13 00 00 00 

+23 2E 11 FE 23 2C 51 FE 23 2A 61 FE 23 28 71 FE 

+23 26 A1 FE 23 24 B1 FE 23 22 C1 FE 23 20 D1 FE 

+23 2E E1 FC 23 2C F1 FC 23 2A 01 FD 23 28 11 FD 

+23 26 C1 FD 23 24 D1 FD 23 22 E1 FD 23 20 F1 FD 

+13 01 01 FC EF 00 40 11 83 20 C1 03 83 22 81 03 

+03 23 41 03 83 23 01 03 03 25 C1 02 83 25 81 02 

+03 26 41 02 83 26 01 02 03 27 C1 01 83 27 81 01 

+03 28 41 01 83 28 01 01 03 2E C1 00 83 2E 81 00 

+03 2F 41 00 83 2F 01 00 13 01 01 04 73 00 20 30 

+17 01 00 F1 13 01 01 75 17 05 00 00 13 05 85 F6 

+73 10 55 30 13 05 00 00 93 05 00 00 17 06 00 00 

+13 06 46 2D 63 0C B5 00 83 26 06 00 23 20 D5 00 

+13 05 45 00 13 06 46 00 6F F0 DF FE 13 05 00 00 

+93 05 80 00 63 08 B5 00 23 20 05 00 13 05 45 00 

+6F F0 5F FF 37 15 00 00 13 05 05 88 73 10 45 30 

+EF 00 C0 1A 6F 00 00 00 13 01 01 FF 23 26 81 00 

+13 04 01 01 13 00 00 00 03 24 C1 00 13 01 01 01 

+67 80 00 00 13 01 01 FF 23 26 81 00 13 04 01 01 

+13 00 00 00 03 24 C1 00 13 01 01 01 67 80 00 00 

+13 01 01 FE 23 2E 81 00 13 04 01 02 23 26 A4 FE 

+83 27 C4 FE 73 90 07 BC 13 00 00 00 03 24 C1 01 

+13 01 01 02 67 80 00 00 13 01 01 FF 23 26 11 00 

+23 24 81 00 13 04 01 01 13 05 00 00 EF F0 5F FC 

+B7 37 00 F0 93 87 87 03 13 07 A0 00 23 A0 E7 00 

+B7 37 00 F0 93 87 C7 03 37 07 02 00 23 A0 E7 00 

+13 07 10 00 23 10 E0 00 13 00 00 00 83 20 C1 00 

+03 24 81 00 13 01 01 01 67 80 00 00 13 01 01 FF 

+23 26 81 00 13 04 01 01 13 00 00 00 03 24 C1 00 

+13 01 01 01 67 80 00 00 13 01 01 FF 23 26 81 00 

+13 04 01 01 13 00 00 00 03 24 C1 00 13 01 01 01 

+67 80 00 00 13 01 01 FE 23 2E 11 00 23 2C 81 00 

+13 04 01 02 93 07 05 00 A3 07 F4 FE 03 47 F4 FE 

+93 07 A0 00 63 16 F7 00 13 05 D0 00 EF F0 9F FD 

+13 00 00 00 B7 67 00 F0 93 87 47 80 03 A7 07 00 

+93 07 10 00 E3 08 F7 FE B7 67 00 F0 93 87 07 80 

+03 47 F4 FE 23 A0 E7 00 13 00 00 00 83 20 C1 01 

+03 24 81 01 13 01 01 02 67 80 00 00 13 01 01 FE 

+23 2E 11 00 23 2C 81 00 13 04 01 02 23 26 A4 FE 

+6F 00 C0 01 83 27 C4 FE 13 87 17 00 23 26 E4 FE 

+83 C7 07 00 13 85 07 00 EF F0 DF F6 83 27 C4 FE 

+83 C7 07 00 E3 90 07 FE 13 00 00 00 83 20 C1 01 

+03 24 81 01 13 01 01 02 67 80 00 00 13 01 01 FF 

+23 26 81 00 13 04 01 01 B7 07 00 26 93 87 47 04 

+13 07 20 40 23 A0 E7 00 B7 07 00 26 93 87 87 04 

+13 07 20 40 23 A0 E7 00 B7 07 00 26 93 87 87 07 

+13 07 20 40 23 A0 E7 00 B7 07 00 26 93 87 C7 07 

+13 07 20 40 23 A0 E7 00 B7 07 00 26 93 87 07 08 

+13 07 20 40 23 A0 E7 00 B7 07 00 26 93 87 47 08 

+13 07 20 40 23 A0 E7 00 B7 07 00 26 93 87 87 09 

+37 27 00 00 13 07 87 80 23 A0 E7 00 B7 07 00 26 

+93 87 47 05 37 27 00 00 13 07 97 80 23 A0 E7 00 

+B7 07 00 26 13 07 10 00 23 A0 E7 00 13 00 00 00 

+B7 07 00 26 03 A7 07 00 93 07 10 00 E3 0A F7 FE 

+B7 07 00 26 93 87 C7 00 83 A6 07 00 B7 07 00 26 

+93 87 C7 00 37 17 00 00 33 E7 E6 00 23 A0 E7 00 

+13 00 00 00 03 24 C1 00 13 01 01 01 67 80 00 00 

diff --git a/verilog/dv/scan_controller_ext/scan_controller_ext.gtkw b/verilog/dv/scan_controller_ext/scan_controller_ext.gtkw
new file mode 100644
index 0000000..c4a3411
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/scan_controller_ext.gtkw
@@ -0,0 +1,46 @@
+[*]
+[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI
+[*] Sun Aug 28 03:59:31 2022
+[*]
+[dumpfile] "/home/matt/work/asic-workshop/shuttle7/tinytapeout-mpw7/verilog/dv/scan_controller_ext/scan_controller_tb.vcd"
+[dumpfile_mtime] "Sun Aug 28 03:59:25 2022"
+[dumpfile_size] 357948
+[savefile] "/home/matt/work/asic-workshop/shuttle7/tinytapeout-mpw7/verilog/dv/scan_controller_ext/scan_controller_ext.gtkw"
+[timestart] 365159000
+[size] 1848 1016
+[pos] -1 -1
+*-18.000000 366175000 -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
+[treeopen] scan_controller_tb.
+[treeopen] scan_controller_tb.uut.
+[treeopen] scan_controller_tb.uut.mprj.
+[sst_width] 423
+[signals_width] 261
+[sst_expanded] 1
+[sst_vpaned_height] 254
+@28
+scan_controller_tb.clk
+scan_controller_tb.driver_sel[1:0]
+scan_controller_tb.fw_ready
+scan_controller_tb.uut.mprj.scan_controller.driver_sel[1:0]
+@800200
+-scan chain
+@28
+scan_controller_tb.uut.mprj.scan_controller.scan_clk
+scan_controller_tb.uut.mprj.scan_controller.scan_data_in
+scan_controller_tb.uut.mprj.scan_controller.scan_data_out
+scan_controller_tb.uut.mprj.scan_controller.scan_latch_en
+scan_controller_tb.uut.mprj.scan_controller.scan_select
+@1000200
+-scan chain
+@800200
+-ext scan chain
+@29
+scan_controller_tb.ext_clk
+scan_controller_tb.ext_data_in
+scan_controller_tb.ext_data_out
+scan_controller_tb.ext_latch
+scan_controller_tb.ext_scan
+@1000200
+-ext scan chain
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/verilog/dv/scan_controller_ext/scan_controller_tb.v b/verilog/dv/scan_controller_ext/scan_controller_tb.v
new file mode 100644
index 0000000..9b02c87
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/scan_controller_tb.v
@@ -0,0 +1,101 @@
+// SPDX-FileCopyrightText: 2020 Efabless Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// SPDX-License-Identifier: Apache-2.0
+
+`default_nettype none
+
+`timescale 1 ns / 1 ps
+
+// change module name to something that suits your project
+module scan_controller_tb;
+    initial begin
+        // change to suit your project
+        $dumpfile ("scan_controller_tb.vcd");
+        $dumpvars (0, scan_controller_tb);
+        #1;
+    end
+
+    reg clk;
+    reg RSTB;
+    reg power1, power2;
+    reg power3, power4;
+
+    wire gpio;
+    wire [37:0] mprj_io;
+
+    ///// convenience signals that match what the cocotb test modules are looking for
+    // change to suit your project. Here's how we can make some nicer named signals for inputs & outputs
+    wire fw_ready = mprj_io[12];
+    wire ext_data_out = mprj_io[29];
+
+    wire [1:0] driver_sel;
+    wire ext_clk, ext_latch, ext_scan, ext_data_in;
+    assign mprj_io[21] = ext_clk;
+    assign mprj_io[22] = ext_data_in;
+    assign mprj_io[23] = ext_scan;
+    assign mprj_io[24] = ext_latch;
+    assign mprj_io[9:8] = driver_sel;
+    /////
+    
+
+    wire flash_csb;
+    wire flash_clk;
+    wire flash_io0;
+    wire flash_io1;
+
+    wire VDD3V3 = power1;
+    wire VDD1V8 = power2;
+    wire USER_VDD3V3 = power3;
+    wire USER_VDD1V8 = power4;
+    wire VSS = 1'b0;
+
+    caravel uut (
+        .vddio    (VDD3V3),
+        .vssio    (VSS),
+        .vdda     (VDD3V3),
+        .vssa     (VSS),
+        .vccd     (VDD1V8),
+        .vssd     (VSS),
+        .vdda1    (USER_VDD3V3),
+        .vdda2    (USER_VDD3V3),
+        .vssa1    (VSS),
+        .vssa2    (VSS),
+        .vccd1    (USER_VDD1V8),
+        .vccd2    (USER_VDD1V8),
+        .vssd1    (VSS),
+        .vssd2    (VSS),
+        .clock    (clk),
+        .gpio     (gpio),
+        .mprj_io  (mprj_io),
+        .flash_csb(flash_csb),
+        .flash_clk(flash_clk),
+        .flash_io0(flash_io0),
+        .flash_io1(flash_io1),
+        .resetb   (RSTB)
+    );
+
+    spiflash #(
+        // change the hex file to match your project
+        .FILENAME("scan_controller.hex")
+    ) spiflash (
+        .csb(flash_csb),
+        .clk(flash_clk),
+        .io0(flash_io0),
+        .io1(flash_io1),
+        .io2(),         // not used
+        .io3()          // not used
+    );
+
+endmodule
+`default_nettype wire
diff --git a/verilog/dv/scan_controller_ext/test_scan_controller.py b/verilog/dv/scan_controller_ext/test_scan_controller.py
new file mode 100644
index 0000000..40af416
--- /dev/null
+++ b/verilog/dv/scan_controller_ext/test_scan_controller.py
@@ -0,0 +1,71 @@
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import RisingEdge, FallingEdge, ClockCycles, with_timeout
+
+@cocotb.test()
+async def test_start(dut):
+    clock = Clock(dut.clk, 25, units="ns") # 40M
+    cocotb.fork(clock.start())
+   
+    dut.driver_sel.value = 0b00 # external
+    dut.ext_clk.value = 0
+    dut.ext_latch.value = 0
+    dut.ext_scan.value = 0
+    dut.ext_data_in.value = 0
+
+    dut.RSTB.value = 0
+    dut.power1.value = 0
+    dut.power2.value = 0
+    dut.power3.value = 0
+    dut.power4.value = 0
+
+    await ClockCycles(dut.clk, 8)
+    dut.power1.value = 1
+    await ClockCycles(dut.clk, 8)
+    dut.power2.value = 1
+    await ClockCycles(dut.clk, 8)
+    dut.power3.value = 1
+    await ClockCycles(dut.clk, 8)
+    dut.power4.value = 1
+
+    await ClockCycles(dut.clk, 80)
+    dut.RSTB.value = 1
+
+    # wait with a timeout for the project to become active
+    await with_timeout(RisingEdge(dut.fw_ready), 650, 'us')
+    print("firmware ready")
+
+    # send some data in
+    dut.ext_data_in.value = 1
+    for i in range(8):
+        dut.ext_clk.value = 1
+        await ClockCycles(dut.clk, 1)
+        dut.ext_clk.value = 0
+        await ClockCycles(dut.clk, 1)
+        if i == 3:
+            dut.ext_data_in.value = 0
+
+    # latch it
+    dut.ext_latch.value = 1
+    await ClockCycles(dut.clk, 1)
+    dut.ext_latch.value = 0
+    await ClockCycles(dut.clk, 1)
+
+    # scan enable
+    dut.ext_scan.value = 1
+    dut.ext_clk.value = 1
+    await ClockCycles(dut.clk, 1)
+    dut.ext_scan.value = 0
+
+    # drive the data out
+    for i in range(8):
+        dut.ext_clk.value = 1
+        await ClockCycles(dut.clk, 1)
+        dut.ext_clk.value = 0
+        await ClockCycles(dut.clk, 1)
+        print(dut.ext_data_out.value)
+        if i < 4:
+            assert(dut.ext_data_out.value == 1)
+        else:
+            assert(dut.ext_data_out.value == 0)
+