diff options
| author | uvok | 2026-01-09 15:18:23 +0100 |
|---|---|---|
| committer | uvok | 2026-01-09 15:18:23 +0100 |
| commit | 6c83fd8730e55de8b1daaac1deb111d3d9bd408e (patch) | |
| tree | 33a3dbc4fd87011b657b193224c3f39c5de6b766 /playground | |
| parent | 678cb2d2d752bbac7625ba9b287762b3acabf116 (diff) | |
move stuff around
Diffstat (limited to 'playground')
| -rw-r--r-- | playground/Makefile | 122 | ||||
| -rw-r--r-- | playground/clkdiv.v | 36 | ||||
| -rw-r--r-- | playground/clock.sdc | 2 | ||||
| -rw-r--r-- | playground/debounce.v | 53 | ||||
| -rw-r--r-- | playground/debounce_tb.v | 76 | ||||
| -rw-r--r-- | playground/fifo.v | 90 | ||||
| -rw-r--r-- | playground/fifo_tb.v | 72 | ||||
| -rw-r--r-- | playground/fizzbuzz.v | 20 | ||||
| -rw-r--r-- | playground/fizzbuzz_tb.v | 48 | ||||
| -rw-r--r-- | playground/led.v | 31 | ||||
| -rw-r--r-- | playground/led_toggle.v | 32 | ||||
| -rw-r--r-- | playground/led_toggle_bouncy.v | 25 | ||||
| -rw-r--r-- | playground/led_toggle_bouncy_tb.v | 50 | ||||
| -rw-r--r-- | playground/led_toggle_nonwork.v | 26 | ||||
| -rw-r--r-- | playground/led_toggle_tb.v | 59 | ||||
| -rw-r--r-- | playground/my_mem.tb.wtf.v | 119 | ||||
| -rw-r--r-- | playground/my_mem.v | 49 | ||||
| -rw-r--r-- | playground/my_mem_tb.v | 99 | ||||
| -rw-r--r-- | playground/par_to_ser.v | 64 | ||||
| -rw-r--r-- | playground/par_to_ser_tb.v | 68 | ||||
| -rw-r--r-- | playground/par_to_ser_to_par_tb.v | 81 | ||||
| -rw-r--r-- | playground/ser_to_par.v | 39 | ||||
| -rw-r--r-- | playground/ser_to_par_tb.v | 62 | ||||
| -rw-r--r-- | playground/template.v | 14 | ||||
| -rw-r--r-- | playground/template_tb.v | 36 | ||||
| -rw-r--r-- | playground/tst_delay.v | 17 | ||||
| -rw-r--r-- | playground/tst_delay_tb.v | 53 |
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 |
