wip for sim tb
diff --git a/configure.py b/configure.py
index 426d647..742dd09 100755
--- a/configure.py
+++ b/configure.py
@@ -68,6 +68,12 @@
         except IndexError:
             return ''
 
+    def get_verilog_names(self, id):
+        try:
+            return ["scan_wrapper_{}.v".format(self.wokwi_ids[id]), "user_module_{}.v".format(self.wokwi_ids[id])]
+        except IndexError:
+            return []
+
     # the latest artifact isn't necessarily the one related to the latest commit, as github
     # could have taken longer to process an older commit than a newer one.
     # so iterate through commits and return the artifact that matches
@@ -311,6 +317,17 @@
             for verilog in verilogs:
                 fh.write(verilog)
 
+        # build complete list of filenames for sim
+        verilog_files = []
+        for i in range(NUM_PROJECTS):
+            verilog_files += self.projects.get_verilog_names(i)
+        verilog_files = CaravelConfig.unique(verilog_files)
+        with open('verilog/includes/includes.rtl.caravel_user_project', 'w') as fh:
+            fh.write('-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v\n')
+            fh.write('-v $(USER_PROJECT_VERILOG)/rtl/scan_controller/scan_controller.v\n')
+            for verilog in verilog_files:
+                fh.write('-v $(USER_PROJECT_VERILOG)/rtl/{}\n'.format(verilog))
+
 
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description="TinyTapeout")
diff --git a/verilog/dv/scan_controller/Makefile b/verilog/dv/scan_controller/Makefile
new file mode 100644
index 0000000..4686307
--- /dev/null
+++ b/verilog/dv/scan_controller/Makefile
@@ -0,0 +1,21 @@
+# cocotb setup
+export COCOTB_REDUCED_LOG_FMT=1
+
+COMPILE_ARGS=-I $(PDK_ROOT)/sky130A/ 
+
+include $(shell cocotb-config --makefiles)/Makefile.sim
+export PYTHONPATH := test
+
+export USER_PROJECT_VERILOG=../../../verilog
+
+test_scan_controller:
+	rm -rf sim_build/
+	mkdir sim_build/
+	iverilog -DMPRJ_IO_PADS=38 -DCOCOTB -o sim_build/sim.vvp -s test_scan_controller_tb -g2012 test_scan_controller_tb.v -I $(PDK_ROOT)/sky130A/ -f../../includes/includes.rtl.caravel_user_project
+	MODULE=test_scan_controller vvp -M $$(cocotb-config --prefix)/cocotb/libs -m libcocotbvpi_icarus sim_build/sim.vvp
+	! grep failure results.xml
+
+clean::
+	rm -rf *vcd sim_build test/__pycache__
+
+.PHONY: clean
diff --git a/verilog/dv/scan_controller/scan_wrapper.py b/verilog/dv/scan_controller/scan_wrapper.py
new file mode 100644
index 0000000..8e9ecce
--- /dev/null
+++ b/verilog/dv/scan_controller/scan_wrapper.py
@@ -0,0 +1,68 @@
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import RisingEdge, FallingEdge, ClockCycles, Timer
+clock_period_ns = 10
+
+def convert_to_int(data):
+    num = 0
+    for i in range(8):
+        if data[7-i]:
+            num += (1 << i)
+    return num
+
+async def scan_clock(dut, cycles=1):
+    for i in range(cycles):
+        dut.clk_in.value = 1
+        await Timer(clock_period_ns, units='ns')
+        dut.clk_in.value = 0
+        await Timer(clock_period_ns, units='ns')
+
+async def preload(dut):
+    # prepare for loading the scan chain
+    dut.scan_select_in.value = 0
+    dut.latch_enable_in.value = 0
+    #dut.data_in.value = 0
+    await scan_clock(dut, 1) 
+
+async def latch(dut):
+    # latch the data from the chain into the module
+    dut.latch_enable_in.value = 1
+    await Timer(clock_period_ns, units='ns')
+    dut.latch_enable_in.value = 0
+
+async def capture_data(dut):
+    # capture the module's output into the scan chain
+    data = []
+    dut.scan_select_in.value = 1
+    await scan_clock(dut, 1) 
+
+    # dump the data out of the chain
+    dut.scan_select_in.value = 0
+    for i in range(8):
+        await scan_clock(dut, 1) 
+        data.append(dut.data_out.value)
+    return data
+
+async def reset(dut):
+    data = [0, 0, 0, 0, 0, 0, 1, 0]
+    await load_data(dut, data)
+    data = [0, 0, 0, 0, 0, 0, 1, 1]
+    await load_data(dut, data)
+    data = [0, 0, 0, 0, 0, 0, 0, 0]
+    await load_data(dut, data)
+
+async def single_cycle(dut):
+    data = [0, 0, 0, 0, 0, 0, 0, 1]
+    await load_data(dut, data)
+    data = [0, 0, 0, 0, 0, 0, 0, 0]
+    await load_data(dut, data)
+
+async def load_data(dut, data):
+    await preload(dut)
+    for i in range(8):
+        dut.data_in.value = data[i]
+        await scan_clock(dut, 1) 
+    # TODO why have to wait an extra cycle here?
+    await scan_clock(dut, 1) 
+    await latch(dut)
+
diff --git a/verilog/dv/scan_controller/test_scan_controller.py b/verilog/dv/scan_controller/test_scan_controller.py
new file mode 100644
index 0000000..a2d55ed
--- /dev/null
+++ b/verilog/dv/scan_controller/test_scan_controller.py
@@ -0,0 +1,39 @@
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import RisingEdge, FallingEdge, ClockCycles
+from scan_wrapper import *
+
+NUM_DESIGNS = 498
+
+# utility functions. using 2nd bit as reset and 1st bit as clock for synchronous design examples
+async def reset(dut):
+    await RisingEdge(dut.ready);
+    dut.inputs = 0b10
+    await RisingEdge(dut.ready);
+    dut.inputs = 0b11
+    await RisingEdge(dut.ready);
+    dut.inputs = 0b0
+
+async def single_cycle(dut):
+    await RisingEdge(dut.ready);
+    dut.inputs = 0b1
+    await RisingEdge(dut.ready);
+    dut.inputs = 0b0
+
+@cocotb.test()
+async def test_lesson_1(dut):
+    clock = Clock(dut.clk, 10, units="us")
+    cocotb.fork(clock.start())
+
+    dut.reset = 1
+    dut.active_select = 0
+    await ClockCycles(dut.clk, 1)
+    dut.reset = 0
+
+    dut.inputs = 0x00
+    await RisingEdge(dut.ready);
+    await RisingEdge(dut.ready);
+    assert dut.outputs == 0x00
+
+    # wait one clock cycle to sync
+    await single_cycle(dut)
diff --git a/verilog/dv/scan_controller/test_scan_controller_tb.v b/verilog/dv/scan_controller/test_scan_controller_tb.v
new file mode 100644
index 0000000..b7d67c2
--- /dev/null
+++ b/verilog/dv/scan_controller/test_scan_controller_tb.v
@@ -0,0 +1,41 @@
+`default_nettype none
+`include "libs.ref/sky130_fd_sc_hd/verilog/primitives.v"
+`include "libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v"
+
+module test_scan_controller_tb(
+    input wire clk,
+    input wire reset,
+
+    input wire [8:0] active_select,
+    input wire [7:0] inputs,
+    output wire [7:0] outputs,
+    output wire ready
+);
+
+    // signals for user_project_wrapper
+    wire [`MPRJ_IO_PADS-1:0] io_in;
+    wire [`MPRJ_IO_PADS-1:0] io_out;
+    wire [`MPRJ_IO_PADS-1:0] io_oeb;
+
+    // map inputs to user_project_wrapper signals
+    assign io_in[20:12] = active_select;
+    assign io_in[28:21] = inputs;
+    assign outputs = io_out[36:29];
+    assign ready = io_out[37];
+
+    `ifdef COCOTB
+    initial begin
+        $dumpfile ("test_scan_controller.vcd");
+        $dumpvars (0, test_scan_controller);
+        #1;
+    end
+    `endif
+
+    user_project_wrapper user_project_wrapper(
+        .wb_clk_i   (clk),
+        .wb_rst_i   (rst),
+        .io_in      (io_in),
+        .io_out     (io_out),
+        .io_oeb     (io_oeb));
+
+endmodule
diff --git a/verilog/includes/includes.rtl.caravel_user_project b/verilog/includes/includes.rtl.caravel_user_project
index 33c0eb3..37d192d 100644
--- a/verilog/includes/includes.rtl.caravel_user_project
+++ b/verilog/includes/includes.rtl.caravel_user_project
@@ -1,5 +1,28 @@
-# Caravel user project includes
--v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v	     
+-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v
 -v $(USER_PROJECT_VERILOG)/rtl/scan_controller/scan_controller.v
-
- 
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_339501025136214612.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_339501025136214612.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_334445762078310996.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_334445762078310996.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_335404063203000914.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_335404063203000914.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_339439899388150354.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_339439899388150354.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_339502597164499540.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_339502597164499540.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_339732875283792466.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_339732875283792466.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_339865743461974612.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_339865743461974612.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_339898704941023827.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_339898704941023827.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_340218629792465491.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_340218629792465491.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_340318610245288530.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_340318610245288530.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_340285391309374034.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_340285391309374034.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_340661930553246290.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_340661930553246290.v
+-v $(USER_PROJECT_VERILOG)/rtl/scan_wrapper_340805072482992722.v
+-v $(USER_PROJECT_VERILOG)/rtl/user_module_340805072482992722.v