diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..af8c0ad8
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "verilog/rtl/multi_project_harness"]
+ path = verilog/rtl/multi_project_harness
+ url = https://github.com/mattvenn/mpw-multi-project-harness
diff --git a/README.md b/README.md
index 447fe6a9..903039b0 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,45 @@
-# CIIC Harness
+# Multi Project Harness
-A template SoC for Google SKY130 free shuttles. It is still WIP. The current SoC architecture is given below.
+* This is a proposal for handling multiple projects in the user project area of the [Caravel harness](https://github.com/efabless/caravel)
+* This is a fork of caravel with https://github.com/mattvenn/mpw-multi-project-harness added to /verilog/rtl/
+* user_project_wrapper is then adjusted to instantiate https://github.com/mattvenn/mpw-multi-project-harness/blob/main/multi_project_harness.v
-
-
-
+
-## Managment SoC
-The managment SoC runs firmware that can be used to:
-- Configure Mega Project I/O pads
-- Observe and control Mega Project signals (through on-chip logic analyzer probes)
-- Control the Mega Project power supply
+# Sub projects
-The memory map of the management SoC can be found [here](verilog/rtl/README)
+See https://github.com/mattvenn/mpw-multi-project-harness/blob/main/.gitmodules
+for the list of currently included projects.
-## Mega Project Area
-This is the user space. It has limited silicon area (TBD, about 3.1mm x 3.8mm) as well as a fixed number of I/O pads (37) and power pads (10). See [the Caravel premliminary datasheet](doc/caravel_datasheet.pdf) for details.
-The repository contains a [sample mega project](/verilog/rtl/user_proj_example.v) that contains a binary 32-bit up counter.
+# Preparation
-
-
-
+See https://github.com/mattvenn/mpw-multi-project-harness for details on adding new projects.
-The firmware running on the Management Area SoC, configures the I/O pads used by the counter and uses the logic probes to observe/control the counter. Three firmware examples are provided:
-1. Configure the Mega Project I/O pads as o/p. Observe the counter value in the testbench: [IO_Ports Test](verilog/dv/caravel/user_proj_example/io_ports).
-2. Configure the Mega Project I/O pads as o/p. Use the Chip LA to load the counter and observe the o/p till it reaches 500: [LA_Test1](verilog/dv/caravel/user_proj_example/la_test1).
-3. Configure the Mega Project I/O pads as o/p. Use the Chip LA to control the clock source and reset signals and observe the counter value for five clock cylcles: [LA_Test2](verilog/dv/caravel/user_proj_example/la_test2).
+# Simulation / Verification.
+
+For formal and cocotb simulation of each module see https://github.com/mattvenn/mpw-multi-project-harness
+
+For caravel system simulation see the tests under verilog/dv/caravel/user_proj_example/
+
+# GDS
+
+See https://github.com/mattvenn/mpw-multi-project-harness/blob/main/docs/hardening.md for details on hardening each module into the main macro.
+This macro's GDS/LEF is then added to openlane/user_project_wrapper
+
+For configuration sees:
+
+* openlane/user_project_wrapper/config.tcl
+* openlane/user_project_wrapper/interactive.tcl
+
+To generate the final GDS, run this command:
+
+ make user_project_wrapper OPENLANE_IMAGE_NAME=openlane:rc5
+
+# Todo
+
+* when the toolchain is working, generate the GDS and add it to the repo
+* info.yaml : update user_level_netlist field
+* add some logo art
+* test input pins in the system simulation
+* how clean does the DRC result need to be?
+* adapt ws2812 for default 10mhz clock rate or be able to update the timing reg
diff --git a/doc/multi-project-harness.png b/doc/multi-project-harness.png
new file mode 100644
index 00000000..4e6967f5
Binary files /dev/null and b/doc/multi-project-harness.png differ
diff --git a/info.yaml b/info.yaml
index ffd05202..8ba42fd4 100644
--- a/info.yaml
+++ b/info.yaml
@@ -1,18 +1,17 @@
---
project:
- description: "A template SoC for Google sponsored Open MPW shuttles for SKY130."
+ description: "Multi project including designs from Matt Venn, Michael Betz & Richard Miller"
foundry: "SkyWater"
- git_url: "https://github.com/efabless/caravel.git"
- organization: "Efabless"
- organization_url: "http://efabless.com"
- owner: "Tim Edwards"
+ git_url: "https://github.com/mattvenn/multi-project-harness.git"
+ organization: ""
+ organization_url: ""
+ owner: "Matt Venn"
process: "SKY130"
- project_name: "Caravel"
+ project_name: "multi-project-harness"
tags:
- "Open MPW"
- - "Test Harness"
- category: "Test Harness"
+ category: "multi project"
top_level_netlist: "verilog/rtl/caravel.v"
- user_level_netlist: "verilog/gl/user_project_wrapper.v"
+ user_level_netlist: "verilog/rtl/multi_project_harness/multi_project_harness.v"
version: "1.00"
cover_image: "doc/ciic_harness.png"
diff --git a/openlane/user_project_wrapper/config.tcl b/openlane/user_project_wrapper/config.tcl
index 50e0bd17..30e400a0 100644
--- a/openlane/user_project_wrapper/config.tcl
+++ b/openlane/user_project_wrapper/config.tcl
@@ -21,10 +21,10 @@ set ::env(VERILOG_FILES) "\
set ::env(VERILOG_FILES_BLACKBOX) "\
$script_dir/../../verilog/rtl/defines.v \
- $script_dir/../../verilog/rtl/user_proj_example.v"
+ $script_dir/../../verilog/rtl/multi_project_harness/multi_project_harness.v"
set ::env(EXTRA_LEFS) "\
- $script_dir/../../lef/user_proj_example.lef"
+ $script_dir/multi_project_harness.lef"
set ::env(EXTRA_GDS_FILES) "\
- $script_dir/../../gds/user_proj_example.gds"
+ $script_dir/multi_project_harness.gds"
diff --git a/openlane/user_project_wrapper/interactive.tcl b/openlane/user_project_wrapper/interactive.tcl
index dd5c0d12..c22a9a7d 100644
--- a/openlane/user_project_wrapper/interactive.tcl
+++ b/openlane/user_project_wrapper/interactive.tcl
@@ -10,7 +10,7 @@ init_floorplan
place_io_ol
-add_macro_placement mprj 1150 1700 N
+add_macro_placement mprj 500 500 N
manual_macro_placement f
diff --git a/verilog/dv/caravel/user_proj_example/ASIC_watch/ASIC_watch.c b/verilog/dv/caravel/user_proj_example/ASIC_watch/ASIC_watch.c
new file mode 100644
index 00000000..323d8607
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ASIC_watch/ASIC_watch.c
@@ -0,0 +1,66 @@
+#include "../../defs.h"
+
+#define PROJECT 5
+#define NB_OUTPUTS 28
+
+#define reg_mprj_oeb0 (*(volatile uint32_t*)0x30000004)
+#define reg_mprj_oeb1 (*(volatile uint32_t*)0x30000008)
+#define reg_mprj_ws2812 (*(volatile uint32_t*)0x30000500)
+/*
+ IO Test:
+ - Configures MPRJ pins
+*/
+
+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 |
+ Output: 0000_0110_0000_1111 (0x1809) = GPIO_MODE_MGNT_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 | 1 |
+
+
+ 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 |
+ */
+
+ /*
+ Inputs
+ 36 - Safe mode
+ 37 - 2^15Hz crystal clock
+
+ Outputs
+ 8 - 14 segment_hxxx
+ 15 - 21 segment_xhxx
+ 22 - 28 segment_xxmx
+ 29 - 35 segment_xxxm
+ */
+ volatile uint32_t *io = ®_mprj_io_0;
+ for (int i = 8 ; i < 8+NB_OUTPUTS ; i++) {
+ io[i] = GPIO_MODE_USER_STD_OUTPUT;
+ }
+
+ io[36] = GPIO_MODE_USER_STD_INPUT_NOPULL;
+ io[37] = GPIO_MODE_USER_STD_INPUT_NOPULL;
+
+ /* Apply configuration */
+ reg_mprj_xfer = 1;
+ while (reg_mprj_xfer == 1);
+
+ // change to project
+ reg_mprj_slave = PROJECT;
+
+ reg_mprj_oeb1 = (1 << 4) + (1 << 5); //GPIO 36 and 37 as inputs
+
+ // use logic analyser bit 0 as reset
+ reg_la0_ena = 0x00000000; // bits 31:0 outputs
+ reg_la0_data = 0x00000001; // reset high is on bit 0
+ reg_la0_data = 0x00000000; // low
+
+}
diff --git a/verilog/dv/caravel/user_proj_example/ASIC_watch/ASIC_watch_tb.v b/verilog/dv/caravel/user_proj_example/ASIC_watch/ASIC_watch_tb.v
new file mode 100644
index 00000000..99517815
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ASIC_watch/ASIC_watch_tb.v
@@ -0,0 +1,162 @@
+`default_nettype none
+
+`timescale 1 ns / 1 ps
+
+`include "caravel.v"
+`include "spiflash.v"
+
+module ASIC_watch_tb;
+ reg clock;
+ reg RSTB;
+ reg power1, power2;
+ reg power3, power4;
+
+ wire gpio;
+ wire [37:0] mprj_io;
+ // inputs
+ reg clk_32768;
+ reg safemode;
+
+ // outputs
+ wire [6:0] segment_hxxx;
+ wire [6:0] segment_xhxx;
+ wire [6:0] segment_xxhx;
+ wire [6:0] segment_xxxh;
+
+ assign segment_hxxx[0] = uut.gpio_control_in[8].pad_gpio_out;
+ assign segment_hxxx[1] = uut.gpio_control_in[9].pad_gpio_out;
+ assign segment_hxxx[2] = uut.gpio_control_in[10].pad_gpio_out;
+ assign segment_hxxx[3] = uut.gpio_control_in[11].pad_gpio_out;
+ assign segment_hxxx[4] = uut.gpio_control_in[12].pad_gpio_out;
+ assign segment_hxxx[5] = uut.gpio_control_in[13].pad_gpio_out;
+ assign segment_hxxx[6] = uut.gpio_control_in[14].pad_gpio_out;
+
+ assign segment_xhxx[0] = uut.gpio_control_in[15].pad_gpio_out;
+ assign segment_xhxx[1] = uut.gpio_control_in[16].pad_gpio_out;
+ assign segment_xhxx[2] = uut.gpio_control_in[17].pad_gpio_out;
+ assign segment_xhxx[3] = uut.gpio_control_in[18].pad_gpio_out;
+ assign segment_xhxx[4] = uut.gpio_control_in[19].pad_gpio_out;
+ assign segment_xhxx[5] = uut.gpio_control_in[20].pad_gpio_out;
+ assign segment_xhxx[6] = uut.gpio_control_in[21].pad_gpio_out;
+
+ assign segment_xxhx[0] = uut.gpio_control_in[22].pad_gpio_out;
+ assign segment_xxhx[1] = uut.gpio_control_in[23].pad_gpio_out;
+ assign segment_xxhx[2] = uut.gpio_control_in[24].pad_gpio_out;
+ assign segment_xxhx[3] = uut.gpio_control_in[25].pad_gpio_out;
+ assign segment_xxhx[4] = uut.gpio_control_in[26].pad_gpio_out;
+ assign segment_xxhx[5] = uut.gpio_control_in[27].pad_gpio_out;
+ assign segment_xxhx[6] = uut.gpio_control_in[28].pad_gpio_out;
+
+ assign segment_xxxh[0] = uut.gpio_control_in[29].pad_gpio_out;
+ assign segment_xxxh[1] = uut.gpio_control_in[30].pad_gpio_out;
+ assign segment_xxxh[2] = uut.gpio_control_in[31].pad_gpio_out;
+ assign segment_xxxh[3] = uut.gpio_control_in[32].pad_gpio_out;
+ assign segment_xxxh[4] = uut.gpio_control_in[33].pad_gpio_out;
+ assign segment_xxxh[5] = uut.gpio_control_in[34].pad_gpio_out;
+ assign segment_xxxh[6] = uut.gpio_control_in[35].pad_gpio_out;
+
+ assign mprj_io[36] = safemode ;
+ assign mprj_io[37] = clk_32768;
+
+ // External clock is used by default. Make this artificially fast for the
+ // simulation. Normally this would be a slow clock and the digital PLL
+ // would be the fast clock.
+
+ always #12.5 clock <= (clock === 1'b0);
+ always #12.5 clk_32768 <= (clk_32768 === 1'b0);
+
+ initial begin
+ clock = 0;
+ clk_32768 = 0;
+ safemode = 0;
+ end
+
+
+ initial begin
+ $dumpfile("ASIC_watch.vcd");
+ $dumpvars(0, ASIC_watch_tb);
+
+ // Repeat cycles of 1000 clock edges as needed to complete testbench
+ repeat (15) begin
+ repeat (1000) @(posedge clock);
+ // $display("+1000 cycles");
+ end
+ $display("%c[1;31m",27);
+ $display ("Monitor: Timeout, Test Mega-Project IO Ports (RTL) Failed");
+ $display("%c[0m",27);
+ $finish;
+ end
+
+ initial begin
+ RSTB <= 1'b0;
+ #2000;
+ RSTB <= 1'b1; // Release reset
+ end
+
+ initial begin // Power-up sequence
+ power1 <= 1'b0;
+ power2 <= 1'b0;
+ power3 <= 1'b0;
+ power4 <= 1'b0;
+ #200;
+ power1 <= 1'b1;
+ #200;
+ power2 <= 1'b1;
+ #200;
+ power3 <= 1'b1;
+ #200;
+ power4 <= 1'b1;
+ end
+
+ wire flash_csb;
+ wire flash_clk;
+ wire flash_io0;
+ wire flash_io1;
+
+ wire VDD1V8;
+ wire VDD3V3;
+ wire VSS;
+
+ assign VDD3V3 = power1;
+ assign VDD1V8 = power2;
+ wire USER_VDD3V3 = power3;
+ wire USER_VDD1V8 = power4;
+ assign 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 (clock),
+ .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 #(
+ .FILENAME("ASIC_watch.hex")
+ ) spiflash (
+ .csb(flash_csb),
+ .clk(flash_clk),
+ .io0(flash_io0),
+ .io1(flash_io1),
+ .io2(), // not used
+ .io3() // not used
+ );
+
+endmodule
diff --git a/verilog/dv/caravel/user_proj_example/ASIC_watch/Makefile b/verilog/dv/caravel/user_proj_example/ASIC_watch/Makefile
new file mode 100644
index 00000000..1e6a2559
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ASIC_watch/Makefile
@@ -0,0 +1,42 @@
+FIRMWARE_PATH = ../..
+RTL_PATH = ../../../../rtl
+IP_PATH = ../../../../ip
+#MP_PATH = ../../../../rtl/multi_project_harness
+BEHAVIOURAL_MODELS = ../../
+
+TOOLCHAIN_PREFIX?=/opt/riscv32ic/bin/riscv32-unknown-elf-
+PDK_PATH?=/home/bscuser/hacking/skywater-pdk
+
+.SUFFIXES:
+
+PATTERN = ASIC_watch
+
+all: ${PATTERN:=.vcd}
+
+hex: ${PATTERN:=.hex}
+
+%.vvp: %_tb.v %.hex
+ iverilog -DFUNCTIONAL -I $(BEHAVIOURAL_MODELS) \
+ -I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+ $< -o $@
+
+%.vcd: %.vvp
+ vvp $<
+
+%.elf: %.c $(FIRMWARE_PATH)/sections.lds $(FIRMWARE_PATH)/start.s
+ $(TOOLCHAIN_PREFIX)gcc -mabi=ilp32 -march=rv32imc -Wl,-Bstatic,-T,$(FIRMWARE_PATH)/sections.lds,--strip-debug -ffreestanding -nostdlib -o $@ $(FIRMWARE_PATH)/start.s $<
+
+%.hex: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O verilog $< $@
+ # to fix flash base address
+ sed -i 's/@10000000/@00000000/g' $@
+
+%.bin: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O binary $< /dev/stdout | tail -c +1048577 > $@
+
+# ---- Clean ----
+
+clean:
+ rm -f *.elf *.hex *.bin *.vvp *.vcd *.log
+
+.PHONY: clean hex all
diff --git a/verilog/dv/caravel/user_proj_example/seven-segment-counter/Makefile b/verilog/dv/caravel/user_proj_example/seven-segment-counter/Makefile
new file mode 100644
index 00000000..359c89cf
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/seven-segment-counter/Makefile
@@ -0,0 +1,42 @@
+FIRMWARE_PATH = ../..
+RTL_PATH = ../../../../rtl
+IP_PATH = ../../../../ip
+#MP_PATH = ../../../../rtl/multi_project_harness
+BEHAVIOURAL_MODELS = ../../
+
+TOOLCHAIN_PREFIX?=/opt/riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-
+PDK_PATH?=~/work/asic-workshop/pdks/sky130A
+
+.SUFFIXES:
+
+PATTERN = seven_segment
+
+all: ${PATTERN:=.vcd}
+
+hex: ${PATTERN:=.hex}
+
+%.vvp: %_tb.v %.hex
+ iverilog -DFUNCTIONAL -I $(BEHAVIOURAL_MODELS) \
+ -I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+ $< -o $@
+
+%.vcd: %.vvp
+ vvp $<
+
+%.elf: %.c $(FIRMWARE_PATH)/sections.lds $(FIRMWARE_PATH)/start.s
+ $(TOOLCHAIN_PREFIX)gcc -mabi=ilp32 -march=rv32imc -Wl,-Bstatic,-T,$(FIRMWARE_PATH)/sections.lds,--strip-debug -ffreestanding -nostdlib -o $@ $(FIRMWARE_PATH)/start.s $<
+
+%.hex: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O verilog $< $@
+ # to fix flash base address
+ sed -i 's/@10000000/@00000000/g' $@
+
+%.bin: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O binary $< /dev/stdout | tail -c +1048577 > $@
+
+# ---- Clean ----
+
+clean:
+ rm -f *.elf *.hex *.bin *.vvp *.vcd *.log
+
+.PHONY: clean hex all
diff --git a/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.c b/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.c
new file mode 100644
index 00000000..4ae44afd
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.c
@@ -0,0 +1,67 @@
+#include "../../defs.h"
+
+/*
+ IO Test:
+ - Configures MPRJ pins
+ - Observes counter value through the LED digits
+*/
+
+#define reg_mprj_7seg (*(volatile uint32_t*)0x30000200)
+
+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 |
+
+ Output: 0000_0110_0000_1111 (0x1809) = GPIO_MODE_MGNT_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 | 1 |
+
+
+ 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 |
+
+ */
+
+ /*
+ Inputs
+
+ system clock
+ system reset
+
+ Outputs
+
+ 14:8 seven segment LEDs
+ */
+
+ reg_mprj_io_8 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_9 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_10 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_11 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_12 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_13 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_14 = GPIO_MODE_USER_STD_OUTPUT;
+
+ /* Apply configuration */
+ reg_mprj_xfer = 1;
+ while (reg_mprj_xfer == 1);
+
+ // change to project 0
+ reg_mprj_slave = 0;
+
+ // use logic analyser to reset the counter
+ reg_la0_ena = 0x00000000; // bits 31:0 outputs
+ reg_la0_data = 0x00000001; // reset high is on bit 0
+ reg_la0_data = 0x00000000; // low
+
+ // update 7seg compare reg to 10
+ reg_mprj_7seg = 10;
+}
+
diff --git a/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.gtkw b/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.gtkw
new file mode 100644
index 00000000..d081a58a
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.gtkw
@@ -0,0 +1,314 @@
+[*]
+[*] GTKWave Analyzer v3.3.108 (w)1999-2020 BSI
+[*] Thu Nov 26 15:54:45 2020
+[*]
+[dumpfile] "/home/matt/work/asic-workshop/caravel/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.vcd"
+[dumpfile_mtime] "Thu Nov 26 15:53:35 2020"
+[dumpfile_size] 101461023
+[savefile] "/home/matt/work/asic-workshop/caravel/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment.gtkw"
+[timestart] 0
+[size] 2488 1529
+[pos] -1 -1
+*-26.000000 192362500 -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] seven_segment_tb.
+[treeopen] seven_segment_tb.uut.
+[treeopen] seven_segment_tb.uut.gpio_control_bidir[0].
+[treeopen] seven_segment_tb.uut.gpio_control_bidir[0].gpio_in_buf.
+[treeopen] seven_segment_tb.uut.gpio_control_bidir[0].gpio_logic_high.
+[treeopen] seven_segment_tb.uut.gpio_control_in[2].
+[treeopen] seven_segment_tb.uut.gpio_control_in[3].
+[treeopen] seven_segment_tb.uut.gpio_control_in[4].
+[treeopen] seven_segment_tb.uut.gpio_control_in[4].gpio_in_buf.
+[treeopen] seven_segment_tb.uut.gpio_control_in[4].gpio_logic_high.
+[treeopen] seven_segment_tb.uut.gpio_control_in[8].
+[treeopen] seven_segment_tb.uut.gpio_control_in[9].
+[treeopen] seven_segment_tb.uut.mprj.
+[treeopen] seven_segment_tb.uut.mprj.mprj.
+[treeopen] seven_segment_tb.uut.mprj.mprj.proj_0.
+[treeopen] seven_segment_tb.uut.soc.
+[treeopen] seven_segment_tb.uut.soc.housekeeping.
+[treeopen] seven_segment_tb.uut.soc.soc.
+[treeopen] seven_segment_tb.uut.soc.soc.cpu.
+[treeopen] seven_segment_tb.uut.soc.soc.cpu.picorv32_core.
+[treeopen] seven_segment_tb.uut.soc.soc.gpio_wb.
+[treeopen] seven_segment_tb.uut.soc.soc.mprj_ctrl.
+[sst_width] 719
+[signals_width] 1098
+[sst_expanded] 1
+[sst_vpaned_height] 710
+@28
+seven_segment_tb.clock
+seven_segment_tb.RSTB
+@200
+-
+@800200
+-wbs
+@28
+seven_segment_tb.uut.mprj.mprj.wb_rst_i
+seven_segment_tb.uut.mprj.mprj.wb_clk_i
+seven_segment_tb.uut.mprj.mprj.wbs_ack_o
+@22
+seven_segment_tb.uut.mprj.mprj.wbs_adr_i[31:0]
+@28
+seven_segment_tb.uut.mprj.mprj.wbs_cyc_i
+@24
+seven_segment_tb.uut.mprj.mprj.wbs_dat_i[31:0]
+@22
+seven_segment_tb.uut.mprj.mprj.wbs_dat_o[31:0]
+seven_segment_tb.uut.mprj.mprj.wbs_sel_i[3:0]
+@28
+seven_segment_tb.uut.mprj.mprj.wbs_stb_i
+seven_segment_tb.uut.mprj.mprj.wbs_we_i
+@1000200
+-wbs
+@800200
+-multi proj control
+@28
+seven_segment_tb.uut.soc.soc.mprj_ctrl.mprj_ctrl.xfer_ctrl
+@1000200
+-multi proj control
+@800200
+-la
+@22
+seven_segment_tb.uut.mprj.mprj.la_data_in[127:0]
+seven_segment_tb.uut.mprj.mprj.la_data_out[127:0]
+seven_segment_tb.uut.mprj.mprj.la_oen[127:0]
+@1000200
+-la
+@22
+seven_segment_tb.uut.soc.soc.cpu.picorv32_core.next_insn_opcode[31:0]
+@200
+-
+@800200
+-multi project
+@22
+seven_segment_tb.uut.mprj.mprj.active_project[7:0]
+@28
+seven_segment_tb.uut.mprj.mprj.wb_rst_i
+@c00022
+seven_segment_tb.uut.mprj.io_in[37:0]
+@28
+(0)seven_segment_tb.uut.mprj.io_in[37:0]
+(1)seven_segment_tb.uut.mprj.io_in[37:0]
+(2)seven_segment_tb.uut.mprj.io_in[37:0]
+(3)seven_segment_tb.uut.mprj.io_in[37:0]
+(4)seven_segment_tb.uut.mprj.io_in[37:0]
+(5)seven_segment_tb.uut.mprj.io_in[37:0]
+(6)seven_segment_tb.uut.mprj.io_in[37:0]
+(7)seven_segment_tb.uut.mprj.io_in[37:0]
+(8)seven_segment_tb.uut.mprj.io_in[37:0]
+(9)seven_segment_tb.uut.mprj.io_in[37:0]
+(10)seven_segment_tb.uut.mprj.io_in[37:0]
+(11)seven_segment_tb.uut.mprj.io_in[37:0]
+(12)seven_segment_tb.uut.mprj.io_in[37:0]
+(13)seven_segment_tb.uut.mprj.io_in[37:0]
+(14)seven_segment_tb.uut.mprj.io_in[37:0]
+(15)seven_segment_tb.uut.mprj.io_in[37:0]
+(16)seven_segment_tb.uut.mprj.io_in[37:0]
+(17)seven_segment_tb.uut.mprj.io_in[37:0]
+(18)seven_segment_tb.uut.mprj.io_in[37:0]
+(19)seven_segment_tb.uut.mprj.io_in[37:0]
+(20)seven_segment_tb.uut.mprj.io_in[37:0]
+(21)seven_segment_tb.uut.mprj.io_in[37:0]
+(22)seven_segment_tb.uut.mprj.io_in[37:0]
+(23)seven_segment_tb.uut.mprj.io_in[37:0]
+(24)seven_segment_tb.uut.mprj.io_in[37:0]
+(25)seven_segment_tb.uut.mprj.io_in[37:0]
+(26)seven_segment_tb.uut.mprj.io_in[37:0]
+(27)seven_segment_tb.uut.mprj.io_in[37:0]
+(28)seven_segment_tb.uut.mprj.io_in[37:0]
+(29)seven_segment_tb.uut.mprj.io_in[37:0]
+(30)seven_segment_tb.uut.mprj.io_in[37:0]
+(31)seven_segment_tb.uut.mprj.io_in[37:0]
+(32)seven_segment_tb.uut.mprj.io_in[37:0]
+(33)seven_segment_tb.uut.mprj.io_in[37:0]
+(34)seven_segment_tb.uut.mprj.io_in[37:0]
+(35)seven_segment_tb.uut.mprj.io_in[37:0]
+(36)seven_segment_tb.uut.mprj.io_in[37:0]
+(37)seven_segment_tb.uut.mprj.io_in[37:0]
+@1401200
+-group_end
+@800022
+seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+@28
+(0)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(1)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(2)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(3)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(4)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(5)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(6)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(7)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(8)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(9)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(10)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(11)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(12)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(13)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(14)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(15)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(16)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(17)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(18)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(19)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(20)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(21)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(22)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(23)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(24)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(25)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(26)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(27)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(28)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(29)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(30)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(31)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(32)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(33)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(34)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(35)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(36)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(37)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+@1001200
+-group_end
+@22
+seven_segment_tb.uut.mprj.mprj.io_oeb[37:0]
+@1000200
+-multi project
+@c00022
+seven_segment_tb.mprj_io[37:0]
+@28
+(0)seven_segment_tb.mprj_io[37:0]
+(1)seven_segment_tb.mprj_io[37:0]
+(2)seven_segment_tb.mprj_io[37:0]
+(3)seven_segment_tb.mprj_io[37:0]
+(4)seven_segment_tb.mprj_io[37:0]
+(5)seven_segment_tb.mprj_io[37:0]
+(6)seven_segment_tb.mprj_io[37:0]
+(7)seven_segment_tb.mprj_io[37:0]
+(8)seven_segment_tb.mprj_io[37:0]
+(9)seven_segment_tb.mprj_io[37:0]
+(10)seven_segment_tb.mprj_io[37:0]
+(11)seven_segment_tb.mprj_io[37:0]
+(12)seven_segment_tb.mprj_io[37:0]
+(13)seven_segment_tb.mprj_io[37:0]
+(14)seven_segment_tb.mprj_io[37:0]
+(15)seven_segment_tb.mprj_io[37:0]
+(16)seven_segment_tb.mprj_io[37:0]
+(17)seven_segment_tb.mprj_io[37:0]
+(18)seven_segment_tb.mprj_io[37:0]
+(19)seven_segment_tb.mprj_io[37:0]
+(20)seven_segment_tb.mprj_io[37:0]
+(21)seven_segment_tb.mprj_io[37:0]
+(22)seven_segment_tb.mprj_io[37:0]
+(23)seven_segment_tb.mprj_io[37:0]
+(24)seven_segment_tb.mprj_io[37:0]
+(25)seven_segment_tb.mprj_io[37:0]
+(26)seven_segment_tb.mprj_io[37:0]
+(27)seven_segment_tb.mprj_io[37:0]
+(28)seven_segment_tb.mprj_io[37:0]
+(29)seven_segment_tb.mprj_io[37:0]
+(30)seven_segment_tb.mprj_io[37:0]
+(31)seven_segment_tb.mprj_io[37:0]
+(32)seven_segment_tb.mprj_io[37:0]
+(33)seven_segment_tb.mprj_io[37:0]
+(34)seven_segment_tb.mprj_io[37:0]
+(35)seven_segment_tb.mprj_io[37:0]
+(36)seven_segment_tb.mprj_io[37:0]
+(37)seven_segment_tb.mprj_io[37:0]
+@c00022
+seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+@28
+(0)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(1)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(2)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(3)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(4)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(5)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(6)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(7)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(8)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(9)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(10)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(11)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(12)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(13)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(14)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(15)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(16)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(17)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(18)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(19)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(20)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(21)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(22)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(23)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(24)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(25)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(26)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(27)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(28)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(29)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(30)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(31)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(32)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(33)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(34)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(35)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(36)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+(37)seven_segment_tb.uut.mprj.mprj.io_out[37:0]
+@1401200
+-group_end
+@22
+seven_segment_tb.uut.mprj.mprj.io_oeb[37:0]
+seven_segment_tb.uut.mprj.mprj.io_in[37:0]
+@1401200
+-group_end
+@800200
+-seven seg
+@28
+seven_segment_tb.uut.mprj.mprj.proj_0.clk
+@22
+seven_segment_tb.uut.mprj.mprj.proj_0.second_counter[23:0]
+@24
+seven_segment_tb.uut.mprj.mprj.proj_0.compare[23:0]
+@28
+seven_segment_tb.uut.mprj.mprj.proj_0.reset
+@800022
+seven_segment_tb.uut.mprj.mprj.proj_0.digit[3:0]
+@28
+(0)seven_segment_tb.uut.mprj.mprj.proj_0.digit[3:0]
+(1)seven_segment_tb.uut.mprj.mprj.proj_0.digit[3:0]
+(2)seven_segment_tb.uut.mprj.mprj.proj_0.digit[3:0]
+(3)seven_segment_tb.uut.mprj.mprj.proj_0.digit[3:0]
+@200
+-
+@800022
+seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+@28
+(0)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+(1)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+(2)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+(3)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+(4)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+(5)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+(6)seven_segment_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+@1001200
+-group_end
+-group_end
+@1000200
+-seven seg
+@800022
+seven_segment_tb.segments[6:0]
+@28
+(0)seven_segment_tb.segments[6:0]
+(1)seven_segment_tb.segments[6:0]
+(2)seven_segment_tb.segments[6:0]
+(3)seven_segment_tb.segments[6:0]
+(4)seven_segment_tb.segments[6:0]
+(5)seven_segment_tb.segments[6:0]
+(6)seven_segment_tb.segments[6:0]
+@1001200
+-group_end
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment_tb.v b/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment_tb.v
new file mode 100644
index 00000000..4bef7724
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/seven-segment-counter/seven_segment_tb.v
@@ -0,0 +1,151 @@
+`default_nettype none
+
+`timescale 1 ns / 1 ps
+
+`include "caravel.v"
+`include "spiflash.v"
+
+module seven_segment_tb;
+ reg clock;
+ reg RSTB;
+ reg power1, power2;
+ reg power3, power4;
+
+ wire gpio;
+ wire [37:0] mprj_io;
+ wire [6:0] segments;
+
+
+ assign segments = {
+ uut.gpio_control_in[14].pad_gpio_out,
+ uut.gpio_control_in[13].pad_gpio_out,
+ uut.gpio_control_in[12].pad_gpio_out,
+ uut.gpio_control_in[11].pad_gpio_out,
+ uut.gpio_control_in[10].pad_gpio_out,
+ uut.gpio_control_in[ 9].pad_gpio_out,
+ uut.gpio_control_in[ 8].pad_gpio_out
+ };
+
+ // External clock is used by default. Make this artificially fast for the
+ // simulation. Normally this would be a slow clock and the digital PLL
+ // would be the fast clock.
+
+ always #12.5 clock <= (clock === 1'b0);
+
+ initial begin
+ clock = 0;
+ end
+
+ initial begin
+ $dumpfile("seven_segment.vcd");
+ $dumpvars(0, seven_segment_tb);
+
+ // Repeat cycles of 1000 clock edges as needed to complete testbench
+ repeat (15) begin
+ repeat (1000) @(posedge clock);
+ // $display("+1000 cycles");
+ end
+ $display("%c[1;31m",27);
+ $display ("Monitor: Timeout, Test Mega-Project IO Ports (RTL) Failed");
+ $display("%c[0m",27);
+ $finish;
+ end
+
+ initial begin
+ // Observe segments counting from 0 to 9
+
+ wait(segments == 7'b0111111);
+ wait(segments == 7'b0000110);
+ wait(segments == 7'b1011011);
+ wait(segments == 7'b1001111);
+ wait(segments == 7'b1100110);
+ wait(segments == 7'b1101101);
+ wait(segments == 7'b1111100);
+ wait(segments == 7'b0000111);
+ wait(segments == 7'b1111111);
+ wait(segments == 7'b1111111);
+ wait(segments == 7'b1100111);
+
+ $display("Monitor: Test 1 Mega-Project IO (RTL) Passed");
+ $finish;
+ end
+
+ initial begin
+ RSTB <= 1'b0;
+ #2000;
+ RSTB <= 1'b1; // Release reset
+ end
+
+ initial begin // Power-up sequence
+ power1 <= 1'b0;
+ power2 <= 1'b0;
+ power3 <= 1'b0;
+ power4 <= 1'b0;
+ #200;
+ power1 <= 1'b1;
+ #200;
+ power2 <= 1'b1;
+ #200;
+ power3 <= 1'b1;
+ #200;
+ power4 <= 1'b1;
+ end
+
+ /*
+ always @(mprj_io) begin
+ #1 $display("MPRJ-IO state = %b ", mprj_io[7:0]);
+ end
+ */
+
+ wire flash_csb;
+ wire flash_clk;
+ wire flash_io0;
+ wire flash_io1;
+
+ wire VDD1V8;
+ wire VDD3V3;
+ wire VSS;
+
+ assign VDD3V3 = power1;
+ assign VDD1V8 = power2;
+ wire USER_VDD3V3 = power3;
+ wire USER_VDD1V8 = power4;
+ assign 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 (clock),
+ .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 #(
+ .FILENAME("seven_segment.hex")
+ ) spiflash (
+ .csb(flash_csb),
+ .clk(flash_clk),
+ .io0(flash_io0),
+ .io1(flash_io1),
+ .io2(), // not used
+ .io3() // not used
+ );
+
+endmodule
diff --git a/verilog/dv/caravel/user_proj_example/spinet/Makefile b/verilog/dv/caravel/user_proj_example/spinet/Makefile
new file mode 100644
index 00000000..cee341c5
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/spinet/Makefile
@@ -0,0 +1,42 @@
+FIRMWARE_PATH = ../..
+RTL_PATH = ../../../../rtl
+IP_PATH = ../../../../ip
+#MP_PATH = ../../../../rtl/multi_project_harness
+BEHAVIOURAL_MODELS = ../../
+
+TOOLCHAIN_PREFIX?=/opt/gcc-riscv/bin/riscv32-unknown-elf-
+PDK_PATH?=/mnt/extra/vlsi/sky130A
+
+.SUFFIXES:
+
+PATTERN = spinet
+
+all: ${PATTERN:=.vcd}
+
+hex: ${PATTERN:=.hex}
+
+%.vvp: %_tb.v %.hex
+ iverilog -DFUNCTIONAL -I $(BEHAVIOURAL_MODELS) \
+ -I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+ $< -o $@
+
+%.vcd: %.vvp
+ vvp $<
+
+%.elf: %.c $(FIRMWARE_PATH)/sections.lds $(FIRMWARE_PATH)/start.s
+ $(TOOLCHAIN_PREFIX)gcc -O2 -mabi=ilp32 -march=rv32imc -Wl,-Bstatic,-T,$(FIRMWARE_PATH)/sections.lds,--strip-debug -ffreestanding -nostdlib -o $@ $(FIRMWARE_PATH)/start.s $<
+
+%.hex: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O verilog $< $@
+ # to fix flash base address
+ sed -i 's/@10000000/@00000000/g' $@
+
+%.bin: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O binary $< /dev/stdout | tail -c +1048577 > $@
+
+# ---- Clean ----
+
+clean:
+ rm -f *.elf *.hex *.bin *.vvp *.vcd *.log
+
+.PHONY: clean hex all
diff --git a/verilog/dv/caravel/user_proj_example/spinet/spinet.c b/verilog/dv/caravel/user_proj_example/spinet/spinet.c
new file mode 100644
index 00000000..29997e67
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/spinet/spinet.c
@@ -0,0 +1,66 @@
+#include "../../defs.h"
+
+/*
+ IO Test:
+ - Configures MPRJ pins
+ - Nothing else to do: spinet is autonomous
+*/
+
+#define PROJECT 3
+#define NUMNODES 6
+
+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 |
+
+ Output: 0000_0110_0000_1111 (0x1809) = GPIO_MODE_MGNT_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 | 1 |
+
+
+ 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 |
+
+ */
+
+ /*
+ Inputs | Outputs
+ Node MOSI SCK SS MISO TXRDY RXRDY
+ 0 0 6 12 18 24 30
+ 1 1 7 13 19 25 31
+ 2 2 8 14 20 26 32
+ 3 3 9 15 21 27 33
+ 4 4 10 16 22 28 34
+ 5 5 11 17 23 29 35
+
+ */
+
+ volatile uint32_t *io = ®_mprj_io_0;
+ for (int i = 0; i < NUMNODES; i++) {
+ for (int j = 0; j <= 12; j += 6)
+ io[i + j] = GPIO_MODE_USER_STD_INPUT_NOPULL;
+ for (int j = 18; j <= 30; j += 6)
+ io[i + j] = GPIO_MODE_USER_STD_OUTPUT;
+ }
+
+ /* Apply configuration */
+ reg_mprj_xfer = 1;
+ while (reg_mprj_xfer == 1);
+
+ // change to project
+ reg_mprj_slave = PROJECT;
+
+ // use logic analyser bit 0 as reset
+ reg_la0_ena = 0x00000000; // bits 31:0 outputs
+ reg_la0_data = 0x00000001; // reset high is on bit 0
+ reg_la0_data = 0x00000000; // low
+
+}
diff --git a/verilog/dv/caravel/user_proj_example/spinet/spinet_tb.v b/verilog/dv/caravel/user_proj_example/spinet/spinet_tb.v
new file mode 100644
index 00000000..35f98a0a
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/spinet/spinet_tb.v
@@ -0,0 +1,240 @@
+`default_nettype none
+
+`timescale 1 ns / 1 ps
+
+`include "caravel.v"
+`include "spiflash.v"
+
+module spinet_tb;
+ reg clock;
+ reg ext_clock;
+ reg RSTB;
+ reg power1, power2;
+ reg power3, power4;
+
+ wire gpio;
+ wire [37:0] mprj_io;
+
+ localparam N = 6;
+
+ // SPI signals for each node
+ reg mosi0, sck0, ss0;
+ wire [N-1:0] mosi, sck, ss;
+ wire [N-1:0] miso, txrdy, rxrdy;
+ assign mprj_io[5:0] = {mosi[5:1],mosi0};
+ assign mprj_io[11:6] = {sck[5:1],sck0};
+ assign mprj_io[17:12] = {ss[5:1],ss0};
+ assign miso = mprj_io[23:18];
+ assign txrdy = mprj_io[29:24];
+ assign rxrdy = mprj_io[35:30];
+
+ initial begin
+ sck0 = 0;
+ ss0 = ~0;
+ mosi0 = 0;
+ end
+
+ // External clock is used by default. Make this artificially fast for the
+ // simulation. Normally this would be a slow clock and the digital PLL
+ // would be the fast clock.
+
+ always #12.5 clock <= (clock === 1'b0);
+ always #6.25 ext_clock <= (ext_clock === 1'b0);
+
+ initial begin
+ clock = 0;
+ ext_clock = 0;
+ end
+
+ initial begin
+ $dumpfile("spinet.vcd");
+ $dumpvars(0, spinet_tb);
+
+ // Repeat cycles of 1000 clock edges as needed to complete testbench
+ repeat (50) begin
+ repeat (1000) @(posedge clock);
+ end
+ $display("%c[1;31m",27);
+ $display ("Monitor: Timeout, Test Failed");
+ $display("%c[0m",27);
+ $finish;
+ end
+
+ reg [15:0] snd, rcv, sent;
+ reg [N-1:0] echoed = 1;
+ initial begin
+ // Wait for initial reset
+ wait (uut.la_data_in_mprj[0] === 1'b1);
+ wait (uut.la_data_in_mprj[0] === 1'b0);
+ // Node 0 sends one packet to each other node
+ for (integer i = 1; i < N; i = i + 1) begin
+ snd <= {2'b10,3'h0,3'h0,8'h40};
+ snd[13:11] <= i;
+ snd[2:0] <= i;
+ #100 ss0 <= 0;
+ sent <= snd;
+ #50;
+ repeat (16) begin
+ #50 mosi0 <= snd[15];
+ sck0 <= 1;
+ #50 rcv <= {rcv[14:0],miso[0]};
+ sck0 <= 0;
+ snd <= snd << 1;
+ end
+ #100 ss0 <= 1;
+ $display("sent %h received: %h", sent, rcv);
+ if (rcv[15])
+ echoed[rcv[10:8]] = 1;
+
+ end
+ #100 ss0 <= 1;
+ // Read packets echoed back by other nodes
+ snd <= 0;
+ while (&echoed == 0) begin
+ wait (rxrdy[0] === 1'b1);
+ ss0 <= 0;
+ #50;
+ repeat (16) begin
+ #50 mosi0 <= snd[15];
+ sck0 <= 1;
+ #50 rcv <= {rcv[14:0],miso[0]};
+ sck0 <= 0;
+ snd <= snd << 1;
+ end
+ sent <= snd;
+ #100 ss0 <= 1;
+ $display("sent %h received: %h", sent, rcv);
+ if (rcv[15])
+ echoed[rcv[10:8]] = 1;
+ end
+ $display("Monitor: Test Passed");
+ $finish;
+ end
+
+ genvar node;
+ generate for (node = 1; node < N; node = node + 1)
+ echo ECHO (mosi[node], sck[node], ss[node], miso[node], txrdy[node], rxrdy[node]);
+ endgenerate
+
+ initial begin
+ RSTB <= 1'b0;
+ #2000;
+ RSTB <= 1'b1; // Release reset
+ end
+
+ initial begin // Power-up sequence
+ power1 <= 1'b0;
+ power2 <= 1'b0;
+ power3 <= 1'b0;
+ power4 <= 1'b0;
+ #200;
+ power1 <= 1'b1;
+ #200;
+ power2 <= 1'b1;
+ #200;
+ power3 <= 1'b1;
+ #200;
+ power4 <= 1'b1;
+ end
+
+ /*
+ always @(mprj_io) begin
+ #1 $display("MPRJ-IO state = %b ", mprj_io[7:0]);
+ end
+ */
+
+ wire flash_csb;
+ wire flash_clk;
+ wire flash_io0;
+ wire flash_io1;
+
+ wire VDD1V8;
+ wire VDD3V3;
+ wire VSS;
+
+ assign VDD3V3 = power1;
+ assign VDD1V8 = power2;
+ assign USER_VDD3V3 = power3;
+ assign USER_VDD1V8 = power4;
+ assign 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 (clock),
+ .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 #(
+ .FILENAME("spinet.hex")
+ ) spiflash (
+ .csb(flash_csb),
+ .clk(flash_clk),
+ .io0(flash_io0),
+ .io1(flash_io1),
+ .io2(), // not used
+ .io3() // not used
+ );
+
+endmodule
+
+// SPI host emulation to read and echo packets
+module echo (
+ output reg mosi,
+ output reg sck,
+ output reg ss,
+ input miso,
+ input txrdy,
+ input rxrdy);
+
+ reg [15:0] pkt = 0;
+ initial begin
+ ss = 1;
+ sck = 0;
+ mosi = 0;
+ end
+ always @(posedge rxrdy) begin
+ // receive a packet
+ ss <= 0;
+ sck <= 0;
+ mosi <= 0;
+ #50;
+ repeat (16) begin
+ #50 sck <= 1;
+ #50 pkt <= {pkt[14:0],miso};
+ sck <= 0;
+ end
+ #100 ss <= 1;
+ // swap sender and receiver address
+ pkt[13:8] <= {pkt[10:8],pkt[13:11]};
+ // send the packet back
+ #50 ss <= 0;
+ #50;
+ repeat (16) begin
+ #50 mosi <= pkt[15];
+ sck <= 1;
+ #50 sck <= 0;
+ pkt <= pkt << 1;
+ end
+ #100 ss <= 1;
+ end
+
+endmodule
diff --git a/verilog/dv/caravel/user_proj_example/vga-clock/Makefile b/verilog/dv/caravel/user_proj_example/vga-clock/Makefile
new file mode 100644
index 00000000..b53df602
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/vga-clock/Makefile
@@ -0,0 +1,42 @@
+FIRMWARE_PATH = ../..
+RTL_PATH = ../../../../rtl
+IP_PATH = ../../../../ip
+#MP_PATH = ../../../../rtl/multi_project_harness
+BEHAVIOURAL_MODELS = ../../
+
+TOOLCHAIN_PREFIX?=/opt/riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-
+PDK_PATH?=~/work/asic-workshop/pdks/sky130A
+
+.SUFFIXES:
+
+PATTERN = vga_clock
+
+all: ${PATTERN:=.vcd}
+
+hex: ${PATTERN:=.hex}
+
+%.vvp: %_tb.v %.hex
+ iverilog -DFUNCTIONAL -I $(BEHAVIOURAL_MODELS) \
+ -I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+ $< -o $@
+
+%.vcd: %.vvp
+ vvp $<
+
+%.elf: %.c $(FIRMWARE_PATH)/sections.lds $(FIRMWARE_PATH)/start.s
+ $(TOOLCHAIN_PREFIX)gcc -mabi=ilp32 -march=rv32imc -Wl,-Bstatic,-T,$(FIRMWARE_PATH)/sections.lds,--strip-debug -ffreestanding -nostdlib -o $@ $(FIRMWARE_PATH)/start.s $<
+
+%.hex: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O verilog $< $@
+ # to fix flash base address
+ sed -i 's/@10000000/@00000000/g' $@
+
+%.bin: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O binary $< /dev/stdout | tail -c +1048577 > $@
+
+# ---- Clean ----
+
+clean:
+ rm -f *.elf *.hex *.bin *.vvp *.vcd *.log
+
+.PHONY: clean hex all
diff --git a/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.c b/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.c
new file mode 100644
index 00000000..a6fe3fc8
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.c
@@ -0,0 +1,77 @@
+#include "../../defs.h"
+
+#define reg_mprj_oeb0 (*(volatile uint32_t*)0x30000004)
+#define reg_mprj_oeb1 (*(volatile uint32_t*)0x30000008)
+/*
+ IO Test:
+ - Configures MPRJ pins
+ - Observes counter value through the LED digits
+*/
+
+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 |
+
+ Output: 0000_0110_0000_1111 (0x1809) = GPIO_MODE_MGNT_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 | 1 |
+
+
+ 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 |
+
+ */
+
+ /*
+ Inputs
+
+ system clock
+ system reset
+ 8 adj hours
+ 9 adj min
+ 10 adj sec
+
+ Outputs
+
+ 11 hsync
+ 12 vsync
+ 13-18 rrggbb
+ */
+
+ reg_mprj_io_8 = GPIO_MODE_USER_STD_INPUT_NOPULL;
+ reg_mprj_io_9 = GPIO_MODE_USER_STD_INPUT_NOPULL;
+ reg_mprj_io_10 = GPIO_MODE_USER_STD_INPUT_NOPULL;
+
+ reg_mprj_io_11 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_12 = GPIO_MODE_USER_STD_OUTPUT;
+
+ reg_mprj_io_13 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_14 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_15 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_16 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_17 = GPIO_MODE_USER_STD_OUTPUT;
+ reg_mprj_io_18 = GPIO_MODE_USER_STD_OUTPUT;
+
+ /* Apply configuration */
+ reg_mprj_xfer = 1;
+ while (reg_mprj_xfer == 1);
+
+ // change to project 2
+ reg_mprj_slave = 2;
+
+ // setup oeb, low for output, high for input
+ reg_mprj_oeb0 = (1 << 8) + (1 << 9) + (1 << 10);
+
+ // use logic analyser to reset the design
+ reg_la0_ena = 0x00000000; // bits 31:0 outputs
+ reg_la0_data = 0x00000001; // reset high is on bit 0
+ reg_la0_data = 0x00000000; // low
+}
diff --git a/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.gtkw b/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.gtkw
new file mode 100644
index 00000000..78a082e6
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.gtkw
@@ -0,0 +1,197 @@
+[*]
+[*] GTKWave Analyzer v3.3.108 (w)1999-2020 BSI
+[*] Thu Nov 26 13:29:36 2020
+[*]
+[dumpfile] "/home/matt/work/asic-workshop/caravel/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.vcd"
+[dumpfile_mtime] "Thu Nov 26 13:28:55 2020"
+[dumpfile_size] 194298414
+[savefile] "/home/matt/work/asic-workshop/caravel/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock.gtkw"
+[timestart] 0
+[size] 1700 1529
+[pos] -1 -1
+*-28.000000 253000000 -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] vga_clock_tb.
+[treeopen] vga_clock_tb.uut.
+[treeopen] vga_clock_tb.uut.gpio_control_bidir[0].gpio_in_buf.
+[treeopen] vga_clock_tb.uut.gpio_control_in[4].gpio_in_buf.
+[treeopen] vga_clock_tb.uut.gpio_control_in[4].gpio_logic_high.
+[treeopen] vga_clock_tb.uut.gpio_control_in[8].
+[treeopen] vga_clock_tb.uut.gpio_control_in[8].gpio_in_buf.
+[treeopen] vga_clock_tb.uut.mprj.
+[treeopen] vga_clock_tb.uut.mprj.mprj.
+[treeopen] vga_clock_tb.uut.mprj.mprj.proj_0.
+[treeopen] vga_clock_tb.uut.mprj.mprj.proj_2.
+[treeopen] vga_clock_tb.uut.soc.
+[treeopen] vga_clock_tb.uut.soc.housekeeping.
+[treeopen] vga_clock_tb.uut.soc.soc.
+[treeopen] vga_clock_tb.uut.soc.soc.cpu.
+[treeopen] vga_clock_tb.uut.soc.soc.cpu.picorv32_core.
+[treeopen] vga_clock_tb.uut.soc.soc.gpio_wb.
+[treeopen] vga_clock_tb.uut.soc.soc.mprj_ctrl.
+[sst_width] 506
+[signals_width] 741
+[sst_expanded] 1
+[sst_vpaned_height] 862
+@28
+vga_clock_tb.clock
+vga_clock_tb.RSTB
+@200
+-
+@800200
+-wbs
+@28
+vga_clock_tb.uut.mprj.mprj.wb_rst_i
+vga_clock_tb.uut.mprj.mprj.wb_clk_i
+vga_clock_tb.uut.mprj.mprj.wbs_ack_o
+@22
+vga_clock_tb.uut.mprj.mprj.wbs_adr_i[31:0]
+@28
+vga_clock_tb.uut.mprj.mprj.wbs_cyc_i
+@24
+vga_clock_tb.uut.mprj.mprj.wbs_dat_i[31:0]
+@22
+vga_clock_tb.uut.mprj.mprj.wbs_dat_o[31:0]
+vga_clock_tb.uut.mprj.mprj.wbs_sel_i[3:0]
+@28
+vga_clock_tb.uut.mprj.mprj.wbs_stb_i
+vga_clock_tb.uut.mprj.mprj.wbs_we_i
+@1000200
+-wbs
+@800200
+-multi proj control
+@28
+vga_clock_tb.uut.soc.soc.mprj_ctrl.mprj_ctrl.xfer_ctrl
+@1000200
+-multi proj control
+@800200
+-la
+@22
+vga_clock_tb.uut.mprj.mprj.la_data_in[127:0]
+vga_clock_tb.uut.mprj.mprj.la_data_out[127:0]
+vga_clock_tb.uut.mprj.mprj.la_oen[127:0]
+@1000200
+-la
+@22
+vga_clock_tb.uut.soc.soc.cpu.picorv32_core.next_insn_opcode[31:0]
+@200
+-
+@800200
+-multi project
+@22
+vga_clock_tb.uut.mprj.mprj.active_project[7:0]
+@23
+vga_clock_tb.uut.mprj.mprj.reg_oeb[37:0]
+@28
+vga_clock_tb.uut.mprj.mprj.wb_rst_i
+@c00022
+vga_clock_tb.uut.mprj.io_in[37:0]
+@28
+(0)vga_clock_tb.uut.mprj.io_in[37:0]
+(1)vga_clock_tb.uut.mprj.io_in[37:0]
+(2)vga_clock_tb.uut.mprj.io_in[37:0]
+(3)vga_clock_tb.uut.mprj.io_in[37:0]
+(4)vga_clock_tb.uut.mprj.io_in[37:0]
+(5)vga_clock_tb.uut.mprj.io_in[37:0]
+(6)vga_clock_tb.uut.mprj.io_in[37:0]
+(7)vga_clock_tb.uut.mprj.io_in[37:0]
+(8)vga_clock_tb.uut.mprj.io_in[37:0]
+(9)vga_clock_tb.uut.mprj.io_in[37:0]
+(10)vga_clock_tb.uut.mprj.io_in[37:0]
+(11)vga_clock_tb.uut.mprj.io_in[37:0]
+(12)vga_clock_tb.uut.mprj.io_in[37:0]
+(13)vga_clock_tb.uut.mprj.io_in[37:0]
+(14)vga_clock_tb.uut.mprj.io_in[37:0]
+(15)vga_clock_tb.uut.mprj.io_in[37:0]
+(16)vga_clock_tb.uut.mprj.io_in[37:0]
+(17)vga_clock_tb.uut.mprj.io_in[37:0]
+(18)vga_clock_tb.uut.mprj.io_in[37:0]
+(19)vga_clock_tb.uut.mprj.io_in[37:0]
+(20)vga_clock_tb.uut.mprj.io_in[37:0]
+(21)vga_clock_tb.uut.mprj.io_in[37:0]
+(22)vga_clock_tb.uut.mprj.io_in[37:0]
+(23)vga_clock_tb.uut.mprj.io_in[37:0]
+(24)vga_clock_tb.uut.mprj.io_in[37:0]
+(25)vga_clock_tb.uut.mprj.io_in[37:0]
+(26)vga_clock_tb.uut.mprj.io_in[37:0]
+(27)vga_clock_tb.uut.mprj.io_in[37:0]
+(28)vga_clock_tb.uut.mprj.io_in[37:0]
+(29)vga_clock_tb.uut.mprj.io_in[37:0]
+(30)vga_clock_tb.uut.mprj.io_in[37:0]
+(31)vga_clock_tb.uut.mprj.io_in[37:0]
+(32)vga_clock_tb.uut.mprj.io_in[37:0]
+(33)vga_clock_tb.uut.mprj.io_in[37:0]
+(34)vga_clock_tb.uut.mprj.io_in[37:0]
+(35)vga_clock_tb.uut.mprj.io_in[37:0]
+(36)vga_clock_tb.uut.mprj.io_in[37:0]
+(37)vga_clock_tb.uut.mprj.io_in[37:0]
+@1401200
+-group_end
+@c00022
+vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+@28
+(0)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(1)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(2)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(3)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(4)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(5)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(6)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(7)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(8)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(9)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(10)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(11)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(12)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(13)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(14)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(15)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(16)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(17)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(18)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(19)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(20)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(21)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(22)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(23)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(24)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(25)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(26)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(27)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(28)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(29)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(30)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(31)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(32)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(33)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(34)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(35)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(36)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+(37)vga_clock_tb.uut.mprj.mprj.io_out[37:0]
+@1401200
+-group_end
+@1000200
+-multi project
+@28
+vga_clock_tb.uut.mprj.mprj.proj_2.reset
+@22
+vga_clock_tb.uut.mprj.mprj.proj_2.hrs_u[3:0]
+@28
+vga_clock_tb.uut.mprj.mprj.proj_2.hrs_d[1:0]
+vga_clock_tb.adj_hrs
+vga_clock_tb.uut.gpio_control_in[8].pad_gpio_in
+vga_clock_tb.uut.gpio_control_in[8].pad_gpio_in
+vga_clock_tb.uut.gpio_control_in[8].pad_gpio_inenb
+vga_clock_tb.uut.gpio_control_in[8].pad_gpio_outenb
+vga_clock_tb.uut.mprj.mprj.proj_2.adj_hrs
+vga_clock_tb.uut.mprj.mprj.proj_2.px_clk
+@24
+vga_clock_tb.uut.mprj.mprj.proj_2.y_px[9:0]
+vga_clock_tb.uut.mprj.mprj.proj_2.x_px[9:0]
+@28
+vga_clock_tb.uut.mprj.mprj.proj_2.activevideo
+vga_clock_tb.uut.mprj.mprj.proj_2.vsync
+vga_clock_tb.hsync
+@22
+vga_clock_tb.rrggbb[5:0]
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock_tb.v b/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock_tb.v
new file mode 100644
index 00000000..6bb03c4a
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/vga-clock/vga_clock_tb.v
@@ -0,0 +1,159 @@
+`default_nettype none
+
+`timescale 1 ns / 1 ps
+
+`include "caravel.v"
+`include "spiflash.v"
+
+module vga_clock_tb;
+ reg clock;
+ reg RSTB;
+ reg power1, power2;
+ reg power3, power4;
+
+ wire gpio;
+ wire [37:0] mprj_io;
+ wire [5:0] rrggbb;
+ wire hsync, vsync;
+
+ assign hsync = uut.gpio_control_in[11].pad_gpio_out;
+ assign vsync = uut.gpio_control_in[12].pad_gpio_out;
+ assign rrggbb = {
+ uut.gpio_control_in[13].pad_gpio_out,
+ uut.gpio_control_in[14].pad_gpio_out,
+ uut.gpio_control_in[15].pad_gpio_out,
+ uut.gpio_control_in[16].pad_gpio_out,
+ uut.gpio_control_in[17].pad_gpio_out,
+ uut.gpio_control_in[18].pad_gpio_out
+ };
+
+ reg adj_hrs = 0;
+ reg adj_min = 0;
+ reg adj_sec = 0;
+ assign mprj_io[8] = adj_hrs;
+ assign mprj_io[9] = adj_min;
+ assign mprj_io[10] = adj_sec;
+/* this doesn't work
+ assign uut.gpio_control_in[ 8].pad_gpio_in = adj_hrs;
+ assign uut.gpio_control_in[ 9].pad_gpio_in = adj_min;
+ assign uut.gpio_control_in[10].pad_gpio_in = adj_sec;
+ */
+
+ // External clock is used by default. Make this artificially fast for the
+ // simulation. Normally this would be a slow clock and the digital PLL
+ // would be the fast clock.
+
+ always #12.5 clock <= (clock === 1'b0);
+
+ initial begin
+ clock = 0;
+ end
+
+ initial begin
+ $dumpfile("vga_clock.vcd");
+ $dumpvars(0, vga_clock_tb);
+
+ // Repeat cycles of 1000 clock edges as needed to complete testbench
+ repeat (15) begin
+ repeat (1000) @(posedge clock);
+ // $display("+1000 cycles");
+ end
+ $display("%c[1;31m",27);
+ $display ("Monitor: Timeout, Test Mega-Project IO Ports (RTL) Failed");
+ $display("%c[0m",27);
+ $finish;
+ end
+
+ initial begin
+ // wait for reset, we have 2 before the project is ready
+ wait(uut.mprj.mprj.proj_2.reset == 1);
+ wait(uut.mprj.mprj.proj_2.reset == 0);
+ wait(uut.mprj.mprj.proj_2.reset == 1);
+ wait(uut.mprj.mprj.proj_2.reset == 0);
+
+ // press all the buttons! button clk_en deboucing is slow so don't want to wait around
+ adj_hrs = 1;
+ adj_min = 1;
+ adj_sec = 1;
+ wait(uut.mprj.mprj.proj_2.hrs_u == 1);
+ $display ("adjusted hours ok");
+ wait(uut.mprj.mprj.proj_2.min_u == 1);
+ $display ("adjusted min ok");
+ wait(uut.mprj.mprj.proj_2.sec_u == 1);
+ $display ("adjusted sec ok");
+
+ end
+
+ initial begin
+ RSTB <= 1'b0;
+ #2000;
+ RSTB <= 1'b1; // Release reset
+ end
+
+ initial begin // Power-up sequence
+ power1 <= 1'b0;
+ power2 <= 1'b0;
+ power3 <= 1'b0;
+ power4 <= 1'b0;
+ #200;
+ power1 <= 1'b1;
+ #200;
+ power2 <= 1'b1;
+ #200;
+ power3 <= 1'b1;
+ #200;
+ power4 <= 1'b1;
+ end
+
+ wire flash_csb;
+ wire flash_clk;
+ wire flash_io0;
+ wire flash_io1;
+
+ wire VDD1V8;
+ wire VDD3V3;
+ wire VSS;
+
+ assign VDD3V3 = power1;
+ assign VDD1V8 = power2;
+ wire USER_VDD3V3 = power3;
+ wire USER_VDD1V8 = power4;
+ assign 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 (clock),
+ .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 #(
+ .FILENAME("vga_clock.hex")
+ ) spiflash (
+ .csb(flash_csb),
+ .clk(flash_clk),
+ .io0(flash_io0),
+ .io1(flash_io1),
+ .io2(), // not used
+ .io3() // not used
+ );
+
+endmodule
diff --git a/verilog/dv/caravel/user_proj_example/ws2812/Makefile b/verilog/dv/caravel/user_proj_example/ws2812/Makefile
new file mode 100644
index 00000000..3a007484
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ws2812/Makefile
@@ -0,0 +1,42 @@
+FIRMWARE_PATH = ../..
+RTL_PATH = ../../../../rtl
+IP_PATH = ../../../../ip
+#MP_PATH = ../../../../rtl/multi_project_harness
+BEHAVIOURAL_MODELS = ../../
+
+TOOLCHAIN_PREFIX?=/opt/riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-
+PDK_PATH?=~/work/asic-workshop/pdks/sky130A
+
+.SUFFIXES:
+
+PATTERN = ws2812
+
+all: ${PATTERN:=.vcd}
+
+hex: ${PATTERN:=.hex}
+
+%.vvp: %_tb.v %.hex
+ iverilog -DFUNCTIONAL -I $(BEHAVIOURAL_MODELS) \
+ -I $(PDK_PATH) -I $(IP_PATH) -I $(RTL_PATH) \
+ $< -o $@
+
+%.vcd: %.vvp
+ vvp $<
+
+%.elf: %.c $(FIRMWARE_PATH)/sections.lds $(FIRMWARE_PATH)/start.s
+ $(TOOLCHAIN_PREFIX)gcc -mabi=ilp32 -march=rv32imc -Wl,-Bstatic,-T,$(FIRMWARE_PATH)/sections.lds,--strip-debug -ffreestanding -nostdlib -o $@ $(FIRMWARE_PATH)/start.s $<
+
+%.hex: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O verilog $< $@
+ # to fix flash base address
+ sed -i 's/@10000000/@00000000/g' $@
+
+%.bin: %.elf
+ $(TOOLCHAIN_PREFIX)objcopy -O binary $< /dev/stdout | tail -c +1048577 > $@
+
+# ---- Clean ----
+
+clean:
+ rm -f *.elf *.hex *.bin *.vvp *.vcd *.log
+
+.PHONY: clean hex all
diff --git a/verilog/dv/caravel/user_proj_example/ws2812/ws2812.c b/verilog/dv/caravel/user_proj_example/ws2812/ws2812.c
new file mode 100644
index 00000000..b04ef429
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ws2812/ws2812.c
@@ -0,0 +1,68 @@
+#include "../../defs.h"
+
+/*
+ IO Test:
+ - Configures MPRJ pins
+ - Observes counter value through the LED digits
+*/
+#define reg_mprj_oeb0 (*(volatile uint32_t*)0x30000004)
+#define reg_mprj_oeb1 (*(volatile uint32_t*)0x30000008)
+
+#define reg_mprj_ws2812 (*(volatile uint32_t*)0x30000100)
+
+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 |
+
+ Output: 0000_0110_0000_1111 (0x1809) = GPIO_MODE_MGNT_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 | 1 |
+
+
+ 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 |
+
+ */
+
+ /*
+ Inputs
+
+ system clock
+ system reset
+
+ Outputs
+
+ 8 data for ws2812
+ */
+
+ reg_mprj_io_8 = GPIO_MODE_USER_STD_OUTPUT;
+
+ /* Apply configuration */
+ reg_mprj_xfer = 1;
+ while (reg_mprj_xfer == 1);
+
+ // change to project 1
+ reg_mprj_slave = 1;
+ // all outputs enabled
+ reg_mprj_oeb0 = 0;
+
+ // use logic analyser to reset the design
+ reg_la0_ena = 0x00000000; // bits 31:0 outputs
+ reg_la0_data = 0x00000001; // reset high is on bit 0
+ reg_la0_data = 0x00000000; // low
+
+ // update led 7
+ uint8_t led_num = 7;
+ uint8_t r = 255;
+ uint8_t g = 10;
+ uint8_t b = 100;
+ reg_mprj_ws2812 = (led_num << 24) + (r << 16) + (g << 8) + b;
+}
diff --git a/verilog/dv/caravel/user_proj_example/ws2812/ws2812.gtkw b/verilog/dv/caravel/user_proj_example/ws2812/ws2812.gtkw
new file mode 100644
index 00000000..65cbad90
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ws2812/ws2812.gtkw
@@ -0,0 +1,250 @@
+[*]
+[*] GTKWave Analyzer v3.3.108 (w)1999-2020 BSI
+[*] Fri Nov 20 10:34:36 2020
+[*]
+[dumpfile] "/home/matt/work/asic-workshop/caravel/verilog/dv/caravel/user_proj_example/ws2812/ws2812.vcd"
+[dumpfile_mtime] "Fri Nov 20 10:32:17 2020"
+[dumpfile_size] 192131563
+[savefile] "/home/matt/work/asic-workshop/caravel/verilog/dv/caravel/user_proj_example/ws2812/ws2812.gtkw"
+[timestart] 0
+[size] 2488 1529
+[pos] -1 -1
+*-26.000000 138637500 -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] ws2812_tb.
+[treeopen] ws2812_tb.uut.
+[treeopen] ws2812_tb.uut.gpio_control_bidir[0].gpio_in_buf.
+[treeopen] ws2812_tb.uut.gpio_control_in[4].gpio_in_buf.
+[treeopen] ws2812_tb.uut.gpio_control_in[4].gpio_logic_high.
+[treeopen] ws2812_tb.uut.mprj.
+[treeopen] ws2812_tb.uut.mprj.mprj.
+[treeopen] ws2812_tb.uut.mprj.mprj.proj_0.
+[treeopen] ws2812_tb.uut.mprj.mprj.proj_2.
+[treeopen] ws2812_tb.uut.soc.
+[treeopen] ws2812_tb.uut.soc.housekeeping.
+[treeopen] ws2812_tb.uut.soc.soc.
+[treeopen] ws2812_tb.uut.soc.soc.cpu.
+[treeopen] ws2812_tb.uut.soc.soc.cpu.picorv32_core.
+[treeopen] ws2812_tb.uut.soc.soc.gpio_wb.
+[treeopen] ws2812_tb.uut.soc.soc.mprj_ctrl.
+[sst_width] 719
+[signals_width] 682
+[sst_expanded] 1
+[sst_vpaned_height] 862
+@28
+ws2812_tb.clock
+ws2812_tb.RSTB
+@200
+-
+@800200
+-wbs
+@28
+ws2812_tb.uut.mprj.mprj.wb_rst_i
+ws2812_tb.uut.mprj.mprj.wb_clk_i
+ws2812_tb.uut.mprj.mprj.wbs_ack_o
+@22
+ws2812_tb.uut.mprj.mprj.wbs_adr_i[31:0]
+@28
+ws2812_tb.uut.mprj.mprj.wbs_cyc_i
+@24
+ws2812_tb.uut.mprj.mprj.wbs_dat_i[31:0]
+@22
+ws2812_tb.uut.mprj.mprj.wbs_dat_o[31:0]
+ws2812_tb.uut.mprj.mprj.wbs_sel_i[3:0]
+@28
+ws2812_tb.uut.mprj.mprj.wbs_stb_i
+ws2812_tb.uut.mprj.mprj.wbs_we_i
+ws2812_tb.uut.mprj.mprj.valid
+@24
+ws2812_tb.uut.mprj.mprj.wstrb[3:0]
+@1000200
+-wbs
+@800200
+-multi proj control
+@28
+ws2812_tb.uut.soc.soc.mprj_ctrl.mprj_ctrl.xfer_ctrl
+@1000200
+-multi proj control
+@800200
+-la
+@22
+ws2812_tb.uut.mprj.mprj.la_data_in[127:0]
+ws2812_tb.uut.mprj.mprj.la_data_out[127:0]
+ws2812_tb.uut.mprj.mprj.la_oen[127:0]
+@1000200
+-la
+@22
+ws2812_tb.uut.soc.soc.cpu.picorv32_core.next_insn_opcode[31:0]
+@200
+-
+@800200
+-multi project
+@22
+ws2812_tb.uut.mprj.mprj.active_project[7:0]
+@28
+ws2812_tb.uut.mprj.mprj.wb_rst_i
+@c00022
+ws2812_tb.uut.mprj.io_in[37:0]
+@28
+(0)ws2812_tb.uut.mprj.io_in[37:0]
+(1)ws2812_tb.uut.mprj.io_in[37:0]
+(2)ws2812_tb.uut.mprj.io_in[37:0]
+(3)ws2812_tb.uut.mprj.io_in[37:0]
+(4)ws2812_tb.uut.mprj.io_in[37:0]
+(5)ws2812_tb.uut.mprj.io_in[37:0]
+(6)ws2812_tb.uut.mprj.io_in[37:0]
+(7)ws2812_tb.uut.mprj.io_in[37:0]
+(8)ws2812_tb.uut.mprj.io_in[37:0]
+(9)ws2812_tb.uut.mprj.io_in[37:0]
+(10)ws2812_tb.uut.mprj.io_in[37:0]
+(11)ws2812_tb.uut.mprj.io_in[37:0]
+(12)ws2812_tb.uut.mprj.io_in[37:0]
+(13)ws2812_tb.uut.mprj.io_in[37:0]
+(14)ws2812_tb.uut.mprj.io_in[37:0]
+(15)ws2812_tb.uut.mprj.io_in[37:0]
+(16)ws2812_tb.uut.mprj.io_in[37:0]
+(17)ws2812_tb.uut.mprj.io_in[37:0]
+(18)ws2812_tb.uut.mprj.io_in[37:0]
+(19)ws2812_tb.uut.mprj.io_in[37:0]
+(20)ws2812_tb.uut.mprj.io_in[37:0]
+(21)ws2812_tb.uut.mprj.io_in[37:0]
+(22)ws2812_tb.uut.mprj.io_in[37:0]
+(23)ws2812_tb.uut.mprj.io_in[37:0]
+(24)ws2812_tb.uut.mprj.io_in[37:0]
+(25)ws2812_tb.uut.mprj.io_in[37:0]
+(26)ws2812_tb.uut.mprj.io_in[37:0]
+(27)ws2812_tb.uut.mprj.io_in[37:0]
+(28)ws2812_tb.uut.mprj.io_in[37:0]
+(29)ws2812_tb.uut.mprj.io_in[37:0]
+(30)ws2812_tb.uut.mprj.io_in[37:0]
+(31)ws2812_tb.uut.mprj.io_in[37:0]
+(32)ws2812_tb.uut.mprj.io_in[37:0]
+(33)ws2812_tb.uut.mprj.io_in[37:0]
+(34)ws2812_tb.uut.mprj.io_in[37:0]
+(35)ws2812_tb.uut.mprj.io_in[37:0]
+(36)ws2812_tb.uut.mprj.io_in[37:0]
+(37)ws2812_tb.uut.mprj.io_in[37:0]
+@1401200
+-group_end
+@c00022
+ws2812_tb.uut.mprj.mprj.io_out[37:0]
+@28
+(0)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(1)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(2)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(3)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(4)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(5)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(6)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(7)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(8)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(9)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(10)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(11)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(12)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(13)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(14)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(15)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(16)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(17)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(18)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(19)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(20)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(21)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(22)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(23)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(24)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(25)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(26)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(27)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(28)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(29)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(30)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(31)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(32)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(33)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(34)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(35)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(36)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+(37)ws2812_tb.uut.mprj.mprj.io_out[37:0]
+@1401200
+-group_end
+@1000200
+-multi project
+@800022
+ws2812_tb.mprj_io[37:0]
+@28
+(0)ws2812_tb.mprj_io[37:0]
+(1)ws2812_tb.mprj_io[37:0]
+(2)ws2812_tb.mprj_io[37:0]
+(3)ws2812_tb.mprj_io[37:0]
+(4)ws2812_tb.mprj_io[37:0]
+(5)ws2812_tb.mprj_io[37:0]
+(6)ws2812_tb.mprj_io[37:0]
+(7)ws2812_tb.mprj_io[37:0]
+(8)ws2812_tb.mprj_io[37:0]
+(9)ws2812_tb.mprj_io[37:0]
+(10)ws2812_tb.mprj_io[37:0]
+(11)ws2812_tb.mprj_io[37:0]
+(12)ws2812_tb.mprj_io[37:0]
+(13)ws2812_tb.mprj_io[37:0]
+(14)ws2812_tb.mprj_io[37:0]
+(15)ws2812_tb.mprj_io[37:0]
+(16)ws2812_tb.mprj_io[37:0]
+(17)ws2812_tb.mprj_io[37:0]
+(18)ws2812_tb.mprj_io[37:0]
+(19)ws2812_tb.mprj_io[37:0]
+(20)ws2812_tb.mprj_io[37:0]
+(21)ws2812_tb.mprj_io[37:0]
+(22)ws2812_tb.mprj_io[37:0]
+(23)ws2812_tb.mprj_io[37:0]
+(24)ws2812_tb.mprj_io[37:0]
+(25)ws2812_tb.mprj_io[37:0]
+(26)ws2812_tb.mprj_io[37:0]
+(27)ws2812_tb.mprj_io[37:0]
+(28)ws2812_tb.mprj_io[37:0]
+(29)ws2812_tb.mprj_io[37:0]
+(30)ws2812_tb.mprj_io[37:0]
+(31)ws2812_tb.mprj_io[37:0]
+(32)ws2812_tb.mprj_io[37:0]
+(33)ws2812_tb.mprj_io[37:0]
+(34)ws2812_tb.mprj_io[37:0]
+(35)ws2812_tb.mprj_io[37:0]
+(36)ws2812_tb.mprj_io[37:0]
+(37)ws2812_tb.mprj_io[37:0]
+@1001200
+-group_end
+@c00200
+-seven seg
+@28
+ws2812_tb.uut.mprj.mprj.proj_0.clk
+@22
+ws2812_tb.uut.mprj.mprj.proj_0.second_counter[23:0]
+@24
+ws2812_tb.uut.mprj.mprj.proj_0.compare[23:0]
+@28
+ws2812_tb.uut.mprj.mprj.proj_0.reset
+@800022
+ws2812_tb.uut.mprj.mprj.proj_0.digit[3:0]
+@28
+(0)ws2812_tb.uut.mprj.mprj.proj_0.digit[3:0]
+(1)ws2812_tb.uut.mprj.mprj.proj_0.digit[3:0]
+(2)ws2812_tb.uut.mprj.mprj.proj_0.digit[3:0]
+(3)ws2812_tb.uut.mprj.mprj.proj_0.digit[3:0]
+@22
+ws2812_tb.uut.mprj.mprj.proj_0.led_out[6:0]
+@1001200
+-group_end
+@1401200
+-seven seg
+@29
+ws2812_tb.uut.mprj.mprj.proj_1.reset
+@800200
+-ws2812
+@24
+ws2812_tb.uut.mprj.mprj.proj_1.bit_counter[11:0]
+ws2812_tb.uut.mprj.mprj.proj_1.rgb_counter[4:0]
+ws2812_tb.uut.mprj.mprj.proj_1.led_counter[2:0]
+@1000200
+-ws2812
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/verilog/dv/caravel/user_proj_example/ws2812/ws2812_tb.v b/verilog/dv/caravel/user_proj_example/ws2812/ws2812_tb.v
new file mode 100644
index 00000000..2f12b51c
--- /dev/null
+++ b/verilog/dv/caravel/user_proj_example/ws2812/ws2812_tb.v
@@ -0,0 +1,115 @@
+`default_nettype none
+
+`timescale 1 ns / 1 ps
+
+`include "caravel.v"
+`include "spiflash.v"
+
+module ws2812_tb;
+ reg clock;
+ reg RSTB;
+ reg power1, power2;
+ reg power3, power4;
+
+ wire gpio;
+ wire [37:0] mprj_io;
+
+ // External clock is used by default. Make this artificially fast for the
+ // simulation. Normally this would be a slow clock and the digital PLL
+ // would be the fast clock.
+
+ always #12.5 clock <= (clock === 1'b0);
+
+ initial begin
+ clock = 0;
+ end
+
+
+ initial begin
+ $dumpfile("ws2812.vcd");
+ $dumpvars(0, ws2812_tb);
+
+ // Repeat cycles of 1000 clock edges as needed to complete testbench
+ repeat (15) begin
+ repeat (1000) @(posedge clock);
+ // $display("+1000 cycles");
+ end
+ $display("%c[1;31m",27);
+ $display ("Monitor: Timeout, Test Mega-Project IO Ports (RTL) Failed");
+ $display("%c[0m",27);
+ $finish;
+ end
+
+ initial begin
+ RSTB <= 1'b0;
+ #2000;
+ RSTB <= 1'b1; // Release reset
+ end
+
+ initial begin // Power-up sequence
+ power1 <= 1'b0;
+ power2 <= 1'b0;
+ power3 <= 1'b0;
+ power4 <= 1'b0;
+ #200;
+ power1 <= 1'b1;
+ #200;
+ power2 <= 1'b1;
+ #200;
+ power3 <= 1'b1;
+ #200;
+ power4 <= 1'b1;
+ end
+
+ wire flash_csb;
+ wire flash_clk;
+ wire flash_io0;
+ wire flash_io1;
+
+ wire VDD1V8;
+ wire VDD3V3;
+ wire VSS;
+
+ assign VDD3V3 = power1;
+ assign VDD1V8 = power2;
+ wire USER_VDD3V3 = power3;
+ wire USER_VDD1V8 = power4;
+ assign 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 (clock),
+ .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 #(
+ .FILENAME("ws2812.hex")
+ ) spiflash (
+ .csb(flash_csb),
+ .clk(flash_clk),
+ .io0(flash_io0),
+ .io1(flash_io1),
+ .io2(), // not used
+ .io3() // not used
+ );
+
+endmodule
diff --git a/verilog/rtl/caravel.v b/verilog/rtl/caravel.v
index 95ca327d..1c04f3b4 100644
--- a/verilog/rtl/caravel.v
+++ b/verilog/rtl/caravel.v
@@ -51,7 +51,7 @@
/*------------------------------*/
/* Include user project here */
/*------------------------------*/
-`include "user_proj_example.v"
+`include "multi_project_harness/includes.v"
// `ifdef USE_OPENRAM
// `include "sram_1rw1r_32_256_8_sky130.v"
diff --git a/verilog/rtl/multi_project_harness b/verilog/rtl/multi_project_harness
new file mode 160000
index 00000000..7cbb5a63
--- /dev/null
+++ b/verilog/rtl/multi_project_harness
@@ -0,0 +1 @@
+Subproject commit 7cbb5a632675e8c28a86d177f8e2075fdf64da80
diff --git a/verilog/rtl/user_project_wrapper.v b/verilog/rtl/user_project_wrapper.v
index b5460f50..c7ef2caa 100644
--- a/verilog/rtl/user_project_wrapper.v
+++ b/verilog/rtl/user_project_wrapper.v
@@ -62,7 +62,7 @@ module user_project_wrapper #(
/* User project is instantiated here */
/*--------------------------------------*/
- user_proj_example mprj (
+ multi_project_harness mprj (
.vdda1(vdda1), // User area 1 3.3V power
.vdda2(vdda2), // User area 2 3.3V power
.vssa1(vssa1), // User area 1 analog ground