test: test bench for game of life cell
to run: `make -C test`
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..7b99d8a
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,3 @@
+__pycache__
+sim_build
+results.xml
\ No newline at end of file
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..22766ba
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,34 @@
+## SPDX-FileCopyrightText: © 2021 Uri Shaked <uri@wokwi.com>
+## SPDX-License-Identifier: MIT
+# See https://docs.cocotb.org/en/stable/quickstart.html for more info
+
+# defaults
+SIM ?= icarus
+TOPLEVEL_LANG ?= verilog
+
+export COCOTB_REDUCED_LOG_FMT=1
+
+ifneq ($(GATES),yes)
+# normal simulation
+VERILOG_SOURCES += $(PWD)/cell_tb.v $(PWD)/../verilog/rtl/user_module_349472166361694804.v $(PWD)/../verilog/rtl/cells.v
+else
+# gate level simulation requires some extra setup
+COMPILE_ARGS += -DGL_TEST
+COMPILE_ARGS += -DFUNCTIONAL
+COMPILE_ARGS += -DUSE_POWER_PINS
+COMPILE_ARGS += -DSIM
+COMPILE_ARGS += -DUNIT_DELAY=#1
+VERILOG_SOURCES += $(PDK_ROOT)/gf180mcuC/libs.ref/gf180mcu_fd_sc_mcu7t5v0/verilog/gf180mcu_fd_sc_mcu7t5v0.v
+
+# the github action copies the gatelevel verilog from /runs/wokwi/results/final/verilog/gl/
+VERILOG_SOURCES += $(PWD)/cell_tb_gl.v $(PWD)/../verilog/gl/tiny_user_project.v
+endif
+
+# TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
+TOPLEVEL = test_cell
+
+# MODULE is the basename of the Python test file
+MODULE = test_cell
+
+# include cocotb's make rules to take care of the simulator setup
+include $(shell cocotb-config --makefiles)/Makefile.sim
diff --git a/test/cell_tb.v b/test/cell_tb.v
new file mode 100644
index 0000000..0c2712e
--- /dev/null
+++ b/test/cell_tb.v
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2022 Uri Shaked <uri@wokwi.com>
+// SPDX-License-Identifier: Apache2.0
+
+`timescale 1ns / 1ps
+//
+`default_nettype none
+
+module test_cell ();
+ reg clk;
+ reg data;
+ reg set;
+ reg reset;
+ reg [7:0] neighbors;
+ wire [7:0] io_out;
+ wire alive = io_out[0];
+ wire notalive = io_out[1];
+
+ user_module_349472166361694804 dut (
+ `ifdef GL_TEST
+ .vccd1( 1'b1),
+ .vssd1( 1'b0),
+ `endif
+ .io_in ({reset, set, clk, neighbors}),
+ .io_out(io_out)
+ );
+
+ initial begin
+ clk = 0;
+ forever begin
+ #5 clk = ~clk;
+ end
+ end
+
+ initial begin
+ $dumpfile("cell_tb.vcd");
+ $dumpvars(0, test_cell);
+ end
+endmodule
\ No newline at end of file
diff --git a/test/cell_tb_gl.v b/test/cell_tb_gl.v
new file mode 100644
index 0000000..e0f2ce3
--- /dev/null
+++ b/test/cell_tb_gl.v
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2022 Uri Shaked <uri@wokwi.com>
+// SPDX-License-Identifier: Apache2.0
+
+`timescale 1ns / 1ps
+//
+`default_nettype none
+
+module test_cell ();
+ reg clk;
+ reg data;
+ reg set;
+ reg reset;
+ reg [7:0] neighbors;
+ wire [37:0] io_out;
+ wire alive = io_out[20];
+ wire notalive = io_out[21];
+
+ tiny_user_project dut (
+ `ifdef GL_TEST
+ .vccd1( 1'b1),
+ .vssd1( 1'b0),
+ `endif
+ .io_in ({reset, set, clk, neighbors, 9'bxxxxxxxxx}),
+ .io_out(io_out)
+ );
+
+ initial begin
+ clk = 0;
+ forever begin
+ #5 clk = ~clk;
+ end
+ end
+
+ initial begin
+ $dumpfile("cell_tb.vcd");
+ $dumpvars(0, test_cell);
+ end
+endmodule
\ No newline at end of file
diff --git a/test/test_cell.py b/test/test_cell.py
new file mode 100644
index 0000000..d6ab2fe
--- /dev/null
+++ b/test/test_cell.py
@@ -0,0 +1,65 @@
+# SPDX-FileCopyrightText: © 2022 Uri Shaked <uri@wokwi.com>
+# SPDX-License-Identifier: Apache2.0
+
+import os
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import ClockCycles, Timer
+
+@cocotb.test()
+async def test_cell(dut):
+ i_neighbors = dut.neighbors
+ i_set = dut.set
+ i_reset = dut.reset
+ o_alive = dut.alive
+ o_notalive = dut.notalive
+ #clk = make_clock(dut, 10)
+
+ i_reset <= 1
+ i_set <= 0
+ i_neighbors <= 0b00000000
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 0
+ assert o_notalive.value == 1
+
+ i_reset <= 0
+ i_set <= 1
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 1
+ assert o_notalive.value == 0
+
+ i_set <= 0
+ i_reset <= 1
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 0
+ assert o_notalive.value == 1
+
+ i_reset <= 0
+ i_neighbors <= 0b00000001
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 0
+
+ i_neighbors <= 0b00000011
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 0
+
+ i_neighbors <= 0b00000111
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 1
+
+ i_neighbors <= 0b00001111
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 0
+
+ i_neighbors <= 0b10011000
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 1
+
+ i_neighbors <= 0b00011000
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 1
+
+ i_neighbors <= 0b00001000
+ await ClockCycles(dut.clk, 2)
+ assert o_alive.value == 0
+