summaryrefslogtreecommitdiff
path: root/playground
diff options
context:
space:
mode:
Diffstat (limited to 'playground')
-rw-r--r--playground/Makefile122
-rw-r--r--playground/clkdiv.v36
-rw-r--r--playground/clock.sdc2
-rw-r--r--playground/debounce.v53
-rw-r--r--playground/debounce_tb.v76
-rw-r--r--playground/fifo.v90
-rw-r--r--playground/fifo_tb.v72
-rw-r--r--playground/fizzbuzz.v20
-rw-r--r--playground/fizzbuzz_tb.v48
-rw-r--r--playground/led.v31
-rw-r--r--playground/led_toggle.v32
-rw-r--r--playground/led_toggle_bouncy.v25
-rw-r--r--playground/led_toggle_bouncy_tb.v50
-rw-r--r--playground/led_toggle_nonwork.v26
-rw-r--r--playground/led_toggle_tb.v59
-rw-r--r--playground/my_mem.tb.wtf.v119
-rw-r--r--playground/my_mem.v49
-rw-r--r--playground/my_mem_tb.v99
-rw-r--r--playground/par_to_ser.v64
-rw-r--r--playground/par_to_ser_tb.v68
-rw-r--r--playground/par_to_ser_to_par_tb.v81
-rw-r--r--playground/ser_to_par.v39
-rw-r--r--playground/ser_to_par_tb.v62
-rw-r--r--playground/template.v14
-rw-r--r--playground/template_tb.v36
-rw-r--r--playground/tst_delay.v17
-rw-r--r--playground/tst_delay_tb.v53
27 files changed, 1443 insertions, 0 deletions
diff --git a/playground/Makefile b/playground/Makefile
new file mode 100644
index 0000000..0ed197e
--- /dev/null
+++ b/playground/Makefile
@@ -0,0 +1,122 @@
+MAKEFLAGS += --no-builtin-rules
+MAKEFLAGS += --no-builtin-variables
+.SUFFIXES:
+
+## Variables
+PROGRAM ?= led
+CST ?= tangnano9k.cst
+
+HARDWARE_SUFFIX := .v
+TESTBENCH_SUFFIX := _tb.v
+
+FLASH_OPTS ?=
+# https://github.com/YosysHQ/apicula/wiki/Nextpnr%E2%80%90Himbaechel-Gowin
+# but
+# https://github.com/YosysHQ/apicula/wiki/gowin_pack says something different. huh?
+DEVICE_NAME ?= GW1NR-LV9QN88PC6/I5
+DEVICE_FAMILY ?= GW1N-9C
+
+# Can't use this for deps:
+# -E $(PROGRAM).deps
+# as yosys needs to have -o specified for this to work
+# but I specify the command manually
+# also, yosys inserts /tmp/ files which makes this useless.
+YOSYS_OPTS = -Q -q -l $*.yosys.log -e "conflict|is used but has no driver"
+NEXTPNR_OPTS = -q -l $*.pnr.log \
+ --sdc clock.sdc \
+ --placed-svg $*.plc.svg \
+ --routed-svg $*.rt.svg
+DEPS := $(wildcard *.dep)
+
+# this is "bad", as it runs the deps target every time
+# probably because of the include below
+# SOURCES := $(wildcard *$(HARDWARE_SUFFIX))
+# DEPS := $(SOURCES:$(HARDWARE_SUFFIX)=.dep)
+
+
+## default target
+all: $(PROGRAM).fs
+
+## dependencies
+
+tangnano9k.cst:
+ wget https://github.com/YosysHQ/apicula/raw/refs/heads/master/examples/tangnano9k.cst || \
+ curl -LO https://github.com/YosysHQ/apicula/raw/refs/heads/master/examples/tangnano9k.cst
+
+## helper targets
+.PHONY: clean flash show
+show: $(PROGRAM)$(HARDWARE_SUFFIX)
+ yosys -p "read_verilog $<; prep; show $(PROGRAM)"
+
+flash: $(PROGRAM).fs
+ openFPGALoader -b tangnano9k $(FLASH_OPTS) $(PROGRAM).fs
+
+clean:
+ rm -rf *.json *.fs *.svg *.log *.dep *.vvp *.lxt2
+
+simu: $(PROGRAM).lxt2
+ gtkwave $< >/dev/null 2>&1
+
+simu2: verilator.$(PROGRAM)/dump.vvp
+ gtkwave $< >/dev/null 2>&1
+
+lint: $(PROGRAM)$(HARDWARE_SUFFIX)
+ verilator --quiet --lint-only -Wall -Wno-PROCASSINIT $(PROGRAM)$(HARDWARE_SUFFIX)
+
+## Patterns
+
+# synthesize
+%.json: %$(HARDWARE_SUFFIX)
+# sh resolvedeps.sh $<
+# only used for dep-generation, output file is needed for dep file, but otherwise useless
+# this must happen *without* synth_gowin, as this includes additional files, which ruin the depfile.
+ yosys -q $< -E $*.dep -o $@ && rm -f $@
+ yosys -p "read_verilog $<; synth_gowin -top $* -json $@" $(YOSYS_OPTS)
+
+# because yosys -E is buggy, or behaves differently than expected,
+# use gawk to force-create deps.
+%.dep: %$(HARDWARE_SUFFIX)
+# sh resolvedeps.sh $<
+ yosys $< -E $*.dep -o $*.json && rm -f $*.json
+
+# place and route?
+%.pnr.json: %.json $(CST)
+ nextpnr-himbaechel --json $< --write $@ \
+ --device $(DEVICE_NAME) --vopt family=$(DEVICE_FAMILY) \
+ --vopt cst=$(CST) \
+ $(NEXTPNR_OPTS) \
+ || \
+ nextpnr-gowin --json $< --write $@ \
+ --device $(DEVICE_NAME) --family $(DEVICE_FAMILY) \
+ --cst $(CST) \
+ $(NEXTPNR_OPTS)
+
+# pack bitstream
+%.fs: %.pnr.json
+ gowin_pack -d $(DEVICE_FAMILY) -o $@ $<
+
+### simulation
+
+%.lxt2: %.vvp
+ ./$< -lxt2
+
+%.vvp: %$(HARDWARE_SUFFIX) %$(TESTBENCH_SUFFIX)
+ iverilog -DDUMP_FILE_NAME='"$*.lxt2"' -g2012 -o $*.vvp $*$(HARDWARE_SUFFIX) $*$(TESTBENCH_SUFFIX)
+
+# verilog unfortunately exits on any warning
+# also on warnings "boohoo, you specified timings in some modules and not in others"
+# since this is fucking annoying, I choose to ignore the exit code.
+verilator.%: %$(HARDWARE_SUFFIX) %$(TESTBENCH_SUFFIX)
+ verilator --quiet -DDUMP_FILE_NAME='"dump.vvp"' --trace --timing --main --exe --Mdir verilator.$(*) $(*)$(TESTBENCH_SUFFIX) || true
+
+# need to specify RM for some reason
+# verilators makefiles doesn't specify the variable
+verilator.%/dump.vvp: verilator.%
+# for whatever reason, some version of vlor started to append _tb.
+ ($(MAKE) -j4 -C verilator.$(*) -f V$(*).mk RM=rm || \
+ $(MAKE) -j4 -C verilator.$(*) -f V$(*)_tb.mk RM=rm)
+ cd verilator.$(*) && (./V$(*) || ./V$(*)_tb)
+
+## inter-file dependencies
+-include $(DEPS)
+-include $($(wildcard *.fs:.fs=.dep)
diff --git a/playground/clkdiv.v b/playground/clkdiv.v
new file mode 100644
index 0000000..b6f1419
--- /dev/null
+++ b/playground/clkdiv.v
@@ -0,0 +1,36 @@
+`timescale 1us/1us
+
+module clkdiv (
+ input rst_i,
+ input clk, // clk input
+ output reg o_divclk // divided output (must be a reg, b/c it needs to keep state)
+);
+
+reg [23:0] counter;
+// CLK is 27 MHz
+// we want a 2Hz signal at the output
+// and pin needs to *toggle twice* within one period
+// 27MHz / 4 = 6750000
+localparam DIVISOR = 24'd6_749_999;
+
+always @(posedge clk or negedge rst_i) begin
+ if (!rst_i)
+ counter <= 24'd0;
+ else if (counter < DIVISOR)
+ counter <= counter + 1'b1;
+ else
+ counter <= 24'd0;
+end
+
+always @(posedge clk or negedge rst_i) begin
+ if (!rst_i)
+ o_divclk <= 1'b0;
+ else if (counter == DIVISOR)
+ o_divclk <= ~o_divclk;
+ else
+ o_divclk <= o_divclk;
+end
+
+
+endmodule
+
diff --git a/playground/clock.sdc b/playground/clock.sdc
new file mode 100644
index 0000000..5e64ad9
--- /dev/null
+++ b/playground/clock.sdc
@@ -0,0 +1,2 @@
+create_clock -period 40.00 -name {clk} [get_ports {clk}]
+create_clock -period 40.00 -name {clk_i} [get_ports {clk_i}]
diff --git a/playground/debounce.v b/playground/debounce.v
new file mode 100644
index 0000000..33dc22e
--- /dev/null
+++ b/playground/debounce.v
@@ -0,0 +1,53 @@
+`timescale 1us/1us
+
+module debounce (
+ input rst_i,
+ input clk_i,
+ input signal_i,
+ output reg signal_o
+);
+
+// number of rising clock edges a signal needs to be stable
+// for it to be output.
+// (+1 for propagation).
+parameter STABLE_PERIOD = 50;
+parameter INIT_SIG_STATE = 1'b1;
+
+reg [31:0] clk_counter;
+reg prev_state;
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i) begin
+ // Learning: I would like to set the output to the input on reset
+ // but then I get
+ // Warning: Async reset value `\signal_i' is not constant!
+ // and a synthesis error.
+ clk_counter <= 0;
+ prev_state <= INIT_SIG_STATE;
+ signal_o <= INIT_SIG_STATE;
+ end else begin
+
+ if (signal_i != prev_state) begin
+ clk_counter <= 0;
+ prev_state <= signal_i;
+ end else begin
+ clk_counter <= clk_counter + 1;
+ end
+
+ if (clk_counter === STABLE_PERIOD) begin
+ signal_o <= signal_i;
+ end
+ end
+end
+
+// Learning?
+// Apparently, (and obviously, when you think about it),
+// it's not possible to drive a signal by two blocks
+// always @(signal_i) begin
+// if (signal_i != prev_state) begin
+// clk_counter <= 0;
+// prev_state <= signal_i;
+// end
+// end
+
+endmodule
diff --git a/playground/debounce_tb.v b/playground/debounce_tb.v
new file mode 100644
index 0000000..fb9430c
--- /dev/null
+++ b/playground/debounce_tb.v
@@ -0,0 +1,76 @@
+`timescale 1us/1us
+
+module debounce_tb;
+
+logic rst_i;
+logic clk_i;
+logic signal_i;
+logic signal_o;
+
+integer i = 0;
+
+debounce #(.STABLE_PERIOD(5)) uut (
+ .rst_i(rst_i),
+ .clk_i(clk_i),
+ .signal_i(signal_i),
+ .signal_o(signal_o)
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="debounce.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+
+ clk_i = 0;
+ rst_i = 1'b1;
+ signal_i = 1'b1;
+ #1
+ rst_i = 1'b0;
+ #1
+ rst_i = 1'b1;
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ // initial key press
+ @(negedge clk_i);
+ signal_i = ~signal_i;
+ assert (signal_o == 1'b1);
+
+ repeat(2) @(negedge clk_i);
+
+ signal_i = ~signal_i;
+ assert (signal_o == 1'b1);
+
+ // try bouncing
+
+ repeat(2) @(negedge clk_i);
+
+ for (i=0; i < 20; i = i + 1) begin
+ @(negedge clk_i);
+ signal_i = ~signal_i;
+ assert (signal_o == 1'b1);
+ end
+ @(negedge clk_i);
+ signal_i = ~signal_i;
+ assert (signal_o == 1'b1);
+
+ repeat(10) @(negedge clk_i);
+ assert (signal_o == 1'b0);
+
+ repeat(10) @(negedge clk_i);
+ signal_i = ~signal_i;
+
+ repeat(10) @(negedge clk_i);
+ assert (signal_o == 1'b1);
+
+ repeat(10) @(negedge clk_i);
+ $finish();
+end
+
+endmodule
diff --git a/playground/fifo.v b/playground/fifo.v
new file mode 100644
index 0000000..bcc2d3f
--- /dev/null
+++ b/playground/fifo.v
@@ -0,0 +1,90 @@
+`timescale 1us/1us
+
+`include "my_mem.v"
+
+module fifo #(
+ parameter DATA_WIDTH = 8,
+ parameter DATA_DEPTH = 1024
+) (
+ input rst_i,
+ input clk_i,
+
+ input write_i,
+ input read_i,
+
+ output empty_o,
+ output full_o,
+
+ //output data_valid_o,
+
+ input [(DATA_WIDTH-1) : 0] data_i,
+ output reg [(DATA_WIDTH-1) : 0] data_o
+
+);
+
+localparam DATA_DEPTH_BITS = $clog2(DATA_DEPTH);
+
+// the -1 will make sure it fits
+/* verilator lint_off WIDTHTRUNC */
+localparam [DATA_DEPTH_BITS-1:0] MAX_ADDRESS = (DATA_DEPTH - 1);
+/* verilator lint_on WIDTHTRUNC */
+
+// need to "count" to number *including* depth
+reg [$clog2(DATA_DEPTH + 1)-1:0] r_count;
+reg [DATA_DEPTH_BITS-1:0] r_read_addr;
+reg [DATA_DEPTH_BITS-1:0] r_write_addr;
+
+assign empty_o = r_count == 0;
+assign full_o = r_count == DATA_DEPTH;
+
+wire int_wr_en = write_i && !full_o;
+wire int_rd_en = read_i && !empty_o;
+
+my_mem #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .DATA_DEPTH(DATA_DEPTH)
+) fifo_mem (
+ .clk_i(clk_i),
+ .write_en_i(int_wr_en),
+ .read_en_i(int_rd_en),
+ .r_read_addr(r_read_addr),
+ .r_write_addr(r_write_addr),
+
+ .data_i(data_i),
+ .data_o(data_o)
+);
+
+initial begin
+ r_count = 0;
+ r_read_addr = 0;
+ r_write_addr = 0;
+end
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i)
+ r_count <= 0;
+ else if (write_i && read_i)
+ // nothing to do
+ // count stays the same
+ ;
+ else if (write_i && !full_o)
+ r_count <= r_count + 1;
+ else if (read_i && !empty_o)
+ r_count <= r_count - 1;
+end
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i)
+ r_read_addr <= 0;
+ else if (read_i && !empty_o)
+ r_read_addr <= (r_read_addr < MAX_ADDRESS) ? (r_read_addr + 1) : 0;
+end
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i)
+ r_write_addr <= 0;
+ else if (write_i && !full_o)
+ r_write_addr <= (r_write_addr < MAX_ADDRESS) ? (r_write_addr + 1) : 0;
+end
+
+endmodule
diff --git a/playground/fifo_tb.v b/playground/fifo_tb.v
new file mode 100644
index 0000000..38199a6
--- /dev/null
+++ b/playground/fifo_tb.v
@@ -0,0 +1,72 @@
+`timescale 1us/1us
+
+module fifo_tb;
+
+reg clk_i;
+reg rst_i;
+
+reg read_i, write_i;
+reg [7:0] data_i;
+
+fifo #(
+ .DATA_WIDTH(8),
+ .DATA_DEPTH(8)
+) uut (
+ .clk_i(clk_i),
+ .rst_i(rst_i),
+ .write_i(write_i),
+ .read_i(read_i),
+ .data_i(data_i),
+
+ .empty_o(),
+ .full_o(),
+ .data_o()
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="fifo.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ rst_i = 1'b1;
+ read_i = 0;
+ write_i = 0;
+ data_i = 0;
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ #15
+
+ for (integer run = 1; run < 3; run++) begin
+
+ write_i = 1;
+ for (integer addr = 0; addr < 10; addr++) begin
+
+ data_i = (run << 4) | addr;
+ #20
+ ;
+ end
+
+ write_i = 0;
+ read_i = 1;
+
+ for (integer addr = 0; addr < 10; addr++) begin
+ #20
+ ;
+ end
+
+ read_i = 0;
+ #20
+ ;
+ end
+ #100
+ $finish();
+end
+
+endmodule
diff --git a/playground/fizzbuzz.v b/playground/fizzbuzz.v
new file mode 100644
index 0000000..0e34c58
--- /dev/null
+++ b/playground/fizzbuzz.v
@@ -0,0 +1,20 @@
+`timescale 1us/1us
+
+module fizzbuzz (
+ input [7:0] num_i,
+ output [7:0] num_o,
+ output fizz_o,
+ output buzz_o,
+ output fizzbuzz_o
+);
+
+wire is_fizz, is_buzz;
+assign is_fizz = num_i % 3 == 0;
+assign is_buzz = num_i % 5 == 0;
+
+assign fizz_o = is_fizz && !is_buzz;
+assign buzz_o = !is_fizz && is_buzz;
+assign fizzbuzz_o = is_fizz && is_buzz;
+assign num_o = (is_fizz || is_buzz) ? 0 : num_i;
+
+endmodule
diff --git a/playground/fizzbuzz_tb.v b/playground/fizzbuzz_tb.v
new file mode 100644
index 0000000..4d612ec
--- /dev/null
+++ b/playground/fizzbuzz_tb.v
@@ -0,0 +1,48 @@
+`timescale 1us/1us
+
+module fizzbuzz_tb;
+
+
+logic [7:0] number;
+logic clk_i;
+
+logic [7:0] num_out;
+
+fizzbuzz uut (
+ .num_i(number),
+ .num_o(num_out),
+ .fizz_o(),
+ .buzz_o(),
+ .fizzbuzz_o()
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="fizzbuzz.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+
+
+ clk_i = 0;
+ number = '0;
+end
+
+always #10 begin
+ clk_i = ~clk_i;
+end
+
+always @(negedge clk_i) begin
+ number <= number + 1;
+ // give iverilog some simulation time...
+ #1;
+ if (number == 3) assert(num_out == 0);
+ if (number == 5) assert(num_out == 0);
+ if (number == 15) assert(num_out == 0);
+
+ if (number == 255) $finish;
+end
+
+endmodule
diff --git a/playground/led.v b/playground/led.v
new file mode 100644
index 0000000..e4f4281
--- /dev/null
+++ b/playground/led.v
@@ -0,0 +1,31 @@
+`timescale 1us/1us
+
+`include "clkdiv.v"
+
+module led (
+ input clk, // clk input
+ input rst_i, // reset input
+ output reg [5:0] led_o // 6 LEDS pin
+);
+
+reg half_sec_clock;
+
+clkdiv half_sec_divider(
+ .rst_i(rst_i),
+ .clk(clk),
+ .o_divclk(half_sec_clock)
+);
+
+always @(posedge half_sec_clock or negedge rst_i) begin
+ if (!rst_i)
+ led_o <= 6'b111111;
+ else
+// else if (counter == 24'd1349_9999) // 0.5s delay
+ led_o[5:0] <= led_o[5:0] - 1;
+// else
+// led_o <= led_o;
+end
+
+
+endmodule
+
diff --git a/playground/led_toggle.v b/playground/led_toggle.v
new file mode 100644
index 0000000..3c54ee4
--- /dev/null
+++ b/playground/led_toggle.v
@@ -0,0 +1,32 @@
+`timescale 1us/1us
+
+// From the book
+// bouncy variant
+
+`include "led_toggle_bouncy.v"
+`include "debounce.v"
+
+module led_toggle (
+ input rst_i,
+ input clk_i,
+ input key_i,
+ output [5:0] led
+);
+parameter STABLE_PERIOD = 50;
+
+wire outsig;
+
+debounce #(.STABLE_PERIOD(STABLE_PERIOD)) db(
+ .rst_i(rst_i),
+ .clk_i(clk_i),
+ .signal_i(key_i),
+ .signal_o(outsig)
+);
+
+led_toggle_bouncy tgler(
+ .clk_i(clk_i),
+ .key_i(outsig),
+ .led(led)
+);
+
+endmodule
diff --git a/playground/led_toggle_bouncy.v b/playground/led_toggle_bouncy.v
new file mode 100644
index 0000000..b05b472
--- /dev/null
+++ b/playground/led_toggle_bouncy.v
@@ -0,0 +1,25 @@
+`timescale 1us/1us
+
+// From the book
+// bouncy variant
+
+module led_toggle_bouncy (
+ input clk_i,
+ input key_i,
+ output [5:0] led
+);
+
+reg r_LED_1 = 1'b1;
+reg r_Switch_1 = 1'b1;
+
+always @(posedge clk_i) begin
+ r_Switch_1 <= key_i;
+ if (key_i == 1'b1 && r_Switch_1 == 1'b0) begin
+ r_LED_1 <= ~r_LED_1;
+ end
+end
+
+assign led[0] = r_LED_1;
+assign led[5:1] = 5'b11111;
+
+endmodule
diff --git a/playground/led_toggle_bouncy_tb.v b/playground/led_toggle_bouncy_tb.v
new file mode 100644
index 0000000..2c04484
--- /dev/null
+++ b/playground/led_toggle_bouncy_tb.v
@@ -0,0 +1,50 @@
+`timescale 1us/1us
+
+module led_toggle_bouncy_tb;
+
+logic clk_i;
+logic key_i;
+logic [5:0] led;
+
+led_toggle_bouncy uut (
+ .clk_i(clk_i),
+ .key_i(key_i),
+ .led(led)
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="led_toggle_bouncy.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+
+ clk_i = 0;
+ key_i = 1'b1;
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ // initial key press
+ @(negedge clk_i);
+ key_i = ~key_i;
+ repeat(2) @(negedge clk_i);
+ key_i = ~key_i;
+
+ // try bouncing
+
+ repeat(2) @(negedge clk_i);
+
+ for (integer i=0; i < 20; i = i + 1) begin
+ @(negedge clk_i);
+ key_i = ~key_i;
+ end
+
+ repeat(5) @(negedge clk_i);
+ $finish();
+end
+
+endmodule
diff --git a/playground/led_toggle_nonwork.v b/playground/led_toggle_nonwork.v
new file mode 100644
index 0000000..5fe458f
--- /dev/null
+++ b/playground/led_toggle_nonwork.v
@@ -0,0 +1,26 @@
+`timescale 1us/1us
+
+//
+// NON-WORKING
+// LED toggle example
+// probably because LED is controlled by two blocks
+//
+
+module led_toggle_nonwork(
+ input clk_i,
+ input key_i,
+ input rst_i,
+ output reg [5:0] led
+);
+
+always @(negedge rst_i) begin
+ if (!rst_i)
+ led <= 6'b111111;
+end
+
+always @(negedge key_i) begin
+ if (!key_i)
+ led[0] <= ~led[0];
+end
+
+endmodule
diff --git a/playground/led_toggle_tb.v b/playground/led_toggle_tb.v
new file mode 100644
index 0000000..78d4f21
--- /dev/null
+++ b/playground/led_toggle_tb.v
@@ -0,0 +1,59 @@
+`timescale 1us/1us
+
+module led_toggle_tb;
+
+logic clk_i;
+logic rst_i;
+logic key_i;
+
+logic [5:0] led;
+
+led_toggle #(.STABLE_PERIOD(2)) uut (
+ .rst_i(rst_i),
+ .clk_i(clk_i),
+ .key_i(key_i),
+ .led(led)
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="led_toggle.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ key_i = 1'b1;
+ #1
+ rst_i = 0;
+ #1
+ rst_i = 1;
+end
+
+always #10 clk_i = ~clk_i;
+
+
+initial begin
+ // initial key press
+ #13
+ key_i = ~key_i;
+ repeat(2) @(negedge clk_i);
+ key_i = ~key_i;
+
+ // try bouncing
+
+ repeat(3) @(negedge clk_i);
+
+ for (integer i=0; i < 19; i = i + 1) begin
+ @(negedge clk_i);
+ key_i = ~key_i;
+ end
+ repeat(10) @(negedge clk_i);
+ key_i = ~key_i;
+
+ repeat(10) @(negedge clk_i);
+ $finish();
+end
+
+endmodule
diff --git a/playground/my_mem.tb.wtf.v b/playground/my_mem.tb.wtf.v
new file mode 100644
index 0000000..8b1aed6
--- /dev/null
+++ b/playground/my_mem.tb.wtf.v
@@ -0,0 +1,119 @@
+// LLM generated, because I'm too lazy to do this manually
+
+`timescale 1us/1ns
+
+module my_mem_tb();
+
+ localparam DATA_WIDTH = 8;
+ localparam DATA_DEPTH = 16;
+ localparam ADDR_WIDTH = $clog2(DATA_DEPTH);
+
+ // DUT signals
+ logic clk;
+ logic write_en_i;
+ logic read_en_i;
+ logic [ADDR_WIDTH-1:0] r_read_addr;
+ logic [ADDR_WIDTH-1:0] r_write_addr;
+ logic [DATA_WIDTH-1:0] data_i;
+ logic [DATA_WIDTH-1:0] data_o;
+
+ // Instantiate DUT
+ my_mem #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .DATA_DEPTH(DATA_DEPTH)
+ ) dut (
+ .clk_i(clk),
+ .write_en_i(write_en_i),
+ .read_en_i(read_en_i),
+ .r_read_addr(r_read_addr),
+ .r_write_addr(r_write_addr),
+ .data_i(data_i),
+ .data_o(data_o)
+ );
+
+ string filename;
+ initial begin
+ `ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+ `else
+ filename="my_mem.lxt2";
+ `endif
+ $dumpfile(filename); $dumpvars();
+ end
+
+ // Clock generator
+ always #10 clk = ~clk;
+
+ // Test sequence
+ initial begin
+ clk = 0;
+ write_en_i = 0;
+ read_en_i = 0;
+ r_read_addr = '0;
+ r_write_addr = '0;
+ data_i = '0;
+
+ repeat (3) @(posedge clk);
+
+ // -------------------------
+ // Write some values
+ // -------------------------
+ @(posedge clk);
+ @(posedge clk);
+ write_en_i = 1;
+ r_write_addr = 10;
+ data_i = 8'hA5;
+
+ @(posedge clk);
+ r_write_addr = 11;
+ data_i = 8'h3C;
+
+ @(posedge clk);
+ write_en_i = 0;
+
+ // -------------------------
+ // Read back values
+ // -------------------------
+
+ // asserts fail in iverilog if I use the posedge stuff,
+ // (but works in verilator).
+ // need an additional clock cycle delay.
+
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ read_en_i = 1;
+ r_read_addr = 10;
+
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ assert (data_o == 8'hA5)
+ else $error("ASSERTION FAILED: addr 10 expected 0xA5, got 0x%02h", data_o);
+
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ r_read_addr = 11;
+
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ assert (data_o == 8'h3C)
+ else $error("ASSERTION FAILED: addr 11 expected 0x3C, got 0x%02h", data_o);
+
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ @(posedge clk);
+ read_en_i = 0;
+
+ repeat (3) @(posedge clk);
+ $finish;
+ end
+
+endmodule
diff --git a/playground/my_mem.v b/playground/my_mem.v
new file mode 100644
index 0000000..1153ec7
--- /dev/null
+++ b/playground/my_mem.v
@@ -0,0 +1,49 @@
+`timescale 1us/1us
+
+`ifndef UVOK_MEMORY
+`define UVOK_MEMORY
+
+module my_mem #(
+ parameter DATA_WIDTH = 8,
+ parameter DATA_DEPTH = 1024
+) (
+ input clk_i,
+
+ input write_en_i,
+ input read_en_i,
+
+ input [$clog2(DATA_DEPTH)-1:0] r_read_addr,
+ input [$clog2(DATA_DEPTH)-1:0] r_write_addr,
+
+ input [(DATA_WIDTH-1) : 0] data_i,
+ output reg [(DATA_WIDTH-1) : 0] data_o
+);
+
+reg [(DATA_WIDTH-1) : 0] r_datastore [(DATA_DEPTH-1) : 0] /* verilator public */;
+
+`ifdef DEBUG
+// for debugging simulations, as iverilog
+// does't show r_datastore
+reg [(DATA_WIDTH-1) : 0] r_cur_r_val;
+reg [(DATA_WIDTH-1) : 0] r_cur_w_val;
+`endif
+
+always @(posedge clk_i) begin
+ if (write_en_i) begin
+ r_datastore[r_write_addr] <= data_i;
+`ifdef DEBUG
+ r_cur_w_val <= data_i;
+`endif
+ end
+
+ if (read_en_i) begin
+ data_o <= r_datastore[r_read_addr];
+`ifdef DEBUG
+ r_cur_r_val <= r_datastore[r_read_addr];
+`endif
+ end
+end
+
+endmodule
+
+`endif
diff --git a/playground/my_mem_tb.v b/playground/my_mem_tb.v
new file mode 100644
index 0000000..c8c2e12
--- /dev/null
+++ b/playground/my_mem_tb.v
@@ -0,0 +1,99 @@
+// LLM generated, because I'm too lazy to do this manually
+
+`timescale 1us/1ns
+
+module my_mem_tb();
+
+ localparam DATA_WIDTH = 8;
+ localparam DATA_DEPTH = 16;
+ localparam ADDR_WIDTH = $clog2(DATA_DEPTH);
+
+ // DUT signals
+ logic clk;
+ logic write_en_i;
+ logic read_en_i;
+ logic [ADDR_WIDTH-1:0] r_read_addr;
+ logic [ADDR_WIDTH-1:0] r_write_addr;
+ logic [DATA_WIDTH-1:0] data_i;
+ logic [DATA_WIDTH-1:0] data_o;
+
+ // Instantiate DUT
+ my_mem #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .DATA_DEPTH(DATA_DEPTH)
+ ) dut (
+ .clk_i(clk),
+ .write_en_i(write_en_i),
+ .read_en_i(read_en_i),
+ .r_read_addr(r_read_addr),
+ .r_write_addr(r_write_addr),
+ .data_i(data_i),
+ .data_o(data_o)
+ );
+
+ string filename;
+ initial begin
+ `ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+ `else
+ filename="my_mem.lxt2";
+ `endif
+ $dumpfile(filename); $dumpvars();
+ end
+
+ // Clock generator
+ always #10 clk = ~clk;
+
+ // Test sequence
+ initial begin
+ clk = 0;
+ write_en_i = 0;
+ read_en_i = 0;
+ r_read_addr = '0;
+ r_write_addr = '0;
+ data_i = '0;
+
+ repeat (3) @(negedge clk);
+
+ // -------------------------
+ // Write some values
+ // -------------------------
+ @(negedge clk);
+ write_en_i = 1;
+ r_write_addr = 10;
+ data_i = 8'hA5;
+
+ @(negedge clk);
+ r_write_addr = 11;
+ data_i = 8'h3C;
+
+ @(negedge clk);
+ write_en_i = 0;
+
+ // -------------------------
+ // Read back values
+ // -------------------------
+
+ @(negedge clk);
+ read_en_i = 1;
+ r_read_addr = 10;
+
+ @(negedge clk);
+ assert (data_o == 8'hA5)
+ else $error("ASSERTION FAILED: addr 10 expected 0xA5, got 0x%02h", data_o);
+
+ @(negedge clk);
+ r_read_addr = 11;
+
+ @(negedge clk);
+ assert (data_o == 8'h3C)
+ else $error("ASSERTION FAILED: addr 11 expected 0x3C, got 0x%02h", data_o);
+
+ @(negedge clk);
+ read_en_i = 0;
+
+ repeat (3) @(negedge clk);
+ $finish;
+ end
+
+endmodule
diff --git a/playground/par_to_ser.v b/playground/par_to_ser.v
new file mode 100644
index 0000000..ab754c9
--- /dev/null
+++ b/playground/par_to_ser.v
@@ -0,0 +1,64 @@
+`timescale 1us/1us
+
+// parallel to serial converter
+
+module par_to_ser #(
+ parameter SHIFT_WIDTH = 8
+) (
+ input rst_i,
+ input clk_i,
+ input data_valid_i,
+ input [(SHIFT_WIDTH-1):0] dat_i,
+ output reg dat_o,
+ output dat_valid_o
+);
+
+// Learning: can't declate parameter here
+// if I want to use it in the input/output list.
+// parameter SHIFT_WIDTH = 8;
+
+reg [(SHIFT_WIDTH-1):0] send_data = {SHIFT_WIDTH{1'b1}};
+// want to count to number *including* width, add 1
+reg [$clog2(SHIFT_WIDTH + 1) - 1:0] count = 0;
+
+// yes, smaller than, the count *to* 8 still takes place
+wire counting = (count != 0) && (count < SHIFT_WIDTH);
+// sending is one byte longer
+wire sending = (count != 0) && (count <= SHIFT_WIDTH);
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i) begin
+ dat_o <= 1'b1;
+ end else if (data_valid_i && count == 0) begin
+ dat_o <= dat_i[0];
+ end else if (sending) begin
+ dat_o <= send_data[0];
+ end
+end
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i) begin
+ send_data <= {SHIFT_WIDTH{1'b1}};
+ end else if (data_valid_i && count == 0) begin
+ send_data <= {1'b1, dat_i[(SHIFT_WIDTH-1):1]};
+ end else if (sending) begin
+ // arbitrary decision: register is filled with a 1
+ send_data <= {1'b1, send_data[(SHIFT_WIDTH-1):1]};
+ end
+end
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i) begin
+ count <= 0;
+ end else if (data_valid_i && count == 0) begin
+ count <= 1;
+ end else if (counting) begin
+ count <= count + 1;
+ end else begin
+ count <= 0;
+ end
+end
+
+assign dat_valid_o = sending;
+
+endmodule
diff --git a/playground/par_to_ser_tb.v b/playground/par_to_ser_tb.v
new file mode 100644
index 0000000..a30e282
--- /dev/null
+++ b/playground/par_to_ser_tb.v
@@ -0,0 +1,68 @@
+`timescale 1us/1us
+
+module par_to_ser_tb;
+
+logic clk_i;
+logic rst_i;
+logic data_valid_i;
+logic [7:0] dat_i;
+logic dat_o;
+
+par_to_ser uut (
+ .clk_i(clk_i),
+ .rst_i(rst_i),
+ .data_valid_i(data_valid_i),
+ .dat_i(dat_i),
+ .dat_o(dat_o),
+ .dat_valid_o()
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="par_to_ser.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ rst_i = 1'b1;
+ data_valid_i = 1'b0;
+
+ #1
+ rst_i = 1'b0;
+ #1
+ rst_i = 1'b1;
+end
+
+always #10 clk_i = ~clk_i;
+
+bit sollbit = 1'b0;
+
+initial begin
+ #13
+ @(negedge clk_i);
+
+ for (integer i = 0; i < 255; i++) begin
+ // clock data in
+ dat_i = i;
+ data_valid_i = 1'b1;
+
+ @(negedge clk_i);
+ data_valid_i = 1'b0;
+
+ for (integer j = 0; j < 8; j++) begin
+ sollbit = (i >> j) & 1;
+ assert(dat_o == sollbit)
+ else $error("Expected bit to be %d, but was %d", sollbit, dat_o);
+
+ @(negedge clk_i);
+ end
+
+ repeat(2) @(negedge clk_i);
+ end
+
+ $finish();
+end
+
+endmodule
diff --git a/playground/par_to_ser_to_par_tb.v b/playground/par_to_ser_to_par_tb.v
new file mode 100644
index 0000000..9a4a3eb
--- /dev/null
+++ b/playground/par_to_ser_to_par_tb.v
@@ -0,0 +1,81 @@
+// converts back and forth
+// parallel > serial > parallel
+
+`timescale 1us/1us
+
+module par_to_ser_to_par_tb;
+
+logic clk_i;
+logic rst_i;
+logic data_valid_i;
+logic [7:0] dat_i;
+
+logic dat_o;
+logic [7:0] dat_o2;
+logic send_valid;
+
+par_to_ser uut (
+ .clk_i(clk_i),
+ .rst_i(rst_i),
+ .data_valid_i(data_valid_i),
+ .dat_i(dat_i),
+ .dat_o(dat_o),
+ .dat_valid_o(send_valid)
+);
+
+ser_to_par uut2 (
+ .clk_i(clk_i),
+ .rst_i(rst_i),
+
+ .dat_valid_i(send_valid),
+ .dat_i(dat_o),
+
+ .dat_o(dat_o2),
+ .dat_valid_o()
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="par_to_ser_to_par.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ rst_i = 1'b1;
+ data_valid_i = 1'b0;
+
+ #1
+ rst_i = 1'b0;
+ #1
+ rst_i = 1'b1;
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ #13;
+ @(negedge clk_i);
+
+ for (integer i = 0; i < 255; i++) begin
+ // clock data in
+ dat_i = i;
+ data_valid_i = 1'b1;
+
+ // wait 1 cycle
+ @(negedge clk_i);
+ data_valid_i = 1'b0;
+
+ // let module do its work
+ repeat(10) @(negedge clk_i);
+
+ assert(i == dat_o2)
+ else $error("Expected output to be h%x, but was h%x", i, dat_o2);
+
+ end
+
+ $finish();
+end
+
+endmodule
diff --git a/playground/ser_to_par.v b/playground/ser_to_par.v
new file mode 100644
index 0000000..f0a0c47
--- /dev/null
+++ b/playground/ser_to_par.v
@@ -0,0 +1,39 @@
+`timescale 1us/1us
+
+// serial to parallel converter
+// Learning:
+// I don't like this.
+// I think I need a signal / way to say "I'm finished"?
+// or generally, an enable pin.
+//
+
+module ser_to_par #(
+ parameter SHIFT_WIDTH = 8
+) (
+ input rst_i,
+ input clk_i,
+
+ input dat_valid_i,
+ input dat_i,
+
+ output reg[(SHIFT_WIDTH - 1):0] dat_o,
+ // ???
+ output dat_valid_o
+);
+
+reg [$clog2(SHIFT_WIDTH) - 1 : 0] count = {$clog2(SHIFT_WIDTH){1'b0}};
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i) begin
+ dat_o <= 8'b0;
+ end else if(dat_valid_i) begin
+ // shift into highest bit first, so it is subsequently shifted down
+ dat_o[SHIFT_WIDTH - 1] <= dat_i;
+ dat_o[(SHIFT_WIDTH - 2):0] <= dat_o[(SHIFT_WIDTH - 1):1];
+ count <= count + 1;
+ end
+end
+
+assign dat_valid_o = count == 0;
+
+endmodule
diff --git a/playground/ser_to_par_tb.v b/playground/ser_to_par_tb.v
new file mode 100644
index 0000000..d39598e
--- /dev/null
+++ b/playground/ser_to_par_tb.v
@@ -0,0 +1,62 @@
+`timescale 1us/1us
+
+module ser_to_par_tb;
+
+logic clk_i;
+logic rst_i;
+logic dat_i;
+logic dat_valid;
+logic [7:0] dat_o;
+
+ser_to_par uut (
+ .clk_i(clk_i),
+ .rst_i(rst_i),
+ .dat_i(dat_i),
+ .dat_o(dat_o),
+ .dat_valid_i(dat_valid),
+ .dat_valid_o()
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="ser_to_par.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ rst_i = 1'b1;
+ dat_i = 1'b1;
+ dat_valid= 1'b0;
+ #1
+ rst_i = 0;
+ #1
+ rst_i = 1;
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ #13;
+ @(negedge clk_i);
+
+ dat_valid = 1'b1;
+ // start data
+ dat_i = 1'b0;
+ @(negedge clk_i);
+ // - 1 clk cycle, 1 bit later:
+ dat_i = 1'b1;
+
+ repeat (7) @(negedge clk_i);
+ dat_valid = 1'b0;
+
+ // - 7 clk cycle, 7 bits later:
+ assert (dat_o == 8'hfe)
+ else $error("Excected dat_o to be hfe, was h%x", dat_o);
+
+ @(negedge clk_i);
+ $finish();
+end
+
+endmodule
diff --git a/playground/template.v b/playground/template.v
new file mode 100644
index 0000000..e6f5280
--- /dev/null
+++ b/playground/template.v
@@ -0,0 +1,14 @@
+`timescale 1us/1us
+
+module template (
+ input rst_i,
+ input clk_i
+);
+
+always @(posedge clk_i or negedge rst_i) begin
+ if (!rst_i) begin
+
+ end
+end
+
+endmodule
diff --git a/playground/template_tb.v b/playground/template_tb.v
new file mode 100644
index 0000000..8bef3a1
--- /dev/null
+++ b/playground/template_tb.v
@@ -0,0 +1,36 @@
+`timescale 1us/1us
+
+module template_tb;
+
+reg clk_i;
+reg rst_i;
+
+template uut (
+ .clk_i(clk_i),
+ .rst_i(rst_i)
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="template.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ rst_i = 1'b1;
+
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ #13
+ @(negedge clk_i);
+
+ #100
+ $finish();
+end
+
+endmodule
diff --git a/playground/tst_delay.v b/playground/tst_delay.v
new file mode 100644
index 0000000..c4017bd
--- /dev/null
+++ b/playground/tst_delay.v
@@ -0,0 +1,17 @@
+`timescale 1us/1us
+
+module tst_delay (
+ input clk_i,
+ input data_i,
+ output reg data_o
+);
+
+initial begin
+ data_o = 0;
+end
+
+always @(posedge clk_i) begin
+ data_o <= data_i;
+end
+
+endmodule
diff --git a/playground/tst_delay_tb.v b/playground/tst_delay_tb.v
new file mode 100644
index 0000000..5812068
--- /dev/null
+++ b/playground/tst_delay_tb.v
@@ -0,0 +1,53 @@
+// try to figure out how iverilog samples edges
+`timescale 1us/1us
+
+module tst_delay_tb;
+
+reg clk_i;
+reg data_i;
+wire data_o;
+
+tst_delay uut (
+ .clk_i(clk_i),
+ .data_i(data_i),
+ .data_o(data_o)
+);
+
+string filename;
+initial begin
+`ifdef DUMP_FILE_NAME
+ filename=`DUMP_FILE_NAME;
+`else
+ filename="tst_delay.lxt2";
+`endif
+ $dumpfile(filename); $dumpvars();
+ clk_i = 0;
+ data_i = 0;
+end
+
+always #10 clk_i = ~clk_i;
+
+initial begin
+ #9
+ data_i = 1;
+ #2
+ data_i = 0;
+
+ /* verilator lint_off INITIALDLY */
+ // note the <= assignment
+ #19
+ data_i <= 1;
+ /* verilator lint_on INITIALDLY */
+
+ #1
+ data_i = 0;
+ // note the = assignment
+ #19
+ data_i = 1;
+ #1
+ data_i = 0;
+ #40
+ $finish();
+end
+
+endmodule