Skip to content

Instantly share code, notes, and snippets.

@kpmcc
Last active December 4, 2022 21:21
Show Gist options
  • Save kpmcc/c1c5c055829edd9c4165202ec8dbd918 to your computer and use it in GitHub Desktop.
Save kpmcc/c1c5c055829edd9c4165202ec8dbd918 to your computer and use it in GitHub Desktop.
Advent of Code 2022 - Day 04 - SystemVerilog + Python + Cocotb
module ascii_integer_decode
(
input logic clk,
input logic rst,
input logic vld,
input logic [7:0] ascii_byte,
input logic terminate,
output logic eg_vld,
output logic [7:0] eg_int
);
initial begin
eg_vld = 0;
eg_int = 0;
end
logic[7:0] int_from_ascii = 0;
always_ff @ (posedge clk) begin
if (rst) begin
int_from_ascii <= 0;
end else begin
case ({terminate, vld})
0: int_from_ascii <= int_from_ascii;
1: int_from_ascii <= (int_from_ascii * 10) + (ascii_byte - 48);
2: int_from_ascii <= 0;
3: int_from_ascii <= 0;
endcase
end
end
always_ff @ (posedge clk) begin
if (rst) begin
eg_int <= 0;
eg_vld <= 0;
end else begin
case ({terminate, vld})
0: begin
eg_int <= eg_int;
eg_vld <= eg_vld;
end
1: begin
eg_int <= eg_int;
eg_vld <= 0;
end
2: begin
eg_int <= int_from_ascii;
eg_vld <= 1;
end
3: begin
eg_vld <= 0;
eg_int <= 0;
end
endcase
end
end
endmodule
module constant_match #
(
parameter CONSTANT [7:0] = 0
)
(
input logic[7:0] data,
output logic match
);
always_comb begin
match <= data == CONSTANT;
end
endmodule
module day_04
(
input logic clk,
input logic rst,
input logic vld,
input logic [7:0] data,
input logic eof,
output logic counts_vld,
output logic [15:0] entirely_contained_count,
output logic [15:0] partially_contained_count
);
initial begin
counts_vld = 0;
partially_contained_count = 0;
entirely_contained_count = 0;
end
logic newline;
logic comma;
logic dash;
symbol_scanner
symbol_scanner
(
.vld(vld),
.text_byte(data),
.newline(newline),
.comma(comma),
.dash(dash)
);
logic terminate_integer_decode;
logic ascii_integer_decode_vld;
always_comb begin
terminate_integer_decode = comma | dash | newline;
ascii_integer_decode_vld = vld & !(terminate_integer_decode);
end
logic integer_from_ascii_vld;
logic[7:0] integer_from_ascii;
ascii_integer_decode
ascii_integer_decode
(
.clk(clk),
.rst(rst),
.vld(ascii_integer_decode_vld),
.ascii_byte(data),
.terminate(terminate_integer_decode),
.eg_vld(integer_from_ascii_vld),
.eg_int(integer_from_ascii)
);
logic [1:0] demux_select = 0;
logic parse_error = 0;
always_ff @ (posedge clk) begin
if (rst) begin
demux_select <= 0;
end else if (vld) begin
case (demux_select)
0: demux_select <= dash ? 1 : 0;
1: demux_select <= comma ? 2 : 1;
2: demux_select <= dash ? 3 : 2;
3: demux_select <= newline ? 0: 3;
endcase
end
end
logic [1:0] demux_select_delayed;
always_ff @ (posedge clk) begin
demux_select_delayed <= demux_select;
end
always_ff @ (posedge clk) begin
if (rst) begin
parse_error <= 0;
end else begin
if (vld) begin
case (demux_select)
0: parse_error <= comma | newline;
1: parse_error <= dash | newline;
2: parse_error <= comma | newline;
3: parse_error <= dash | comma;
endcase
end
end
end
// numbers for the elf work ranges
logic [3:0] value_updated;
logic [7:0] a_left;
logic [7:0] a_right;
logic [7:0] b_left;
logic [7:0] b_right;
demux_4 #
(.WIDTH(8))
demux_4
(
.clk(clk),
.vld(integer_from_ascii_vld),
.data_in(integer_from_ascii),
.sel(demux_select_delayed),
.data_updated(value_updated),
.data_out({b_right, b_left, a_right, a_left})
);
logic range_intersection_vld;
logic partial_intersect;
logic complete_intersect;
range_intersection
range_intersection
(
.clk(clk),
.vld (value_updated[3]),
.a_lo (a_left),
.a_hi (a_right),
.b_lo (b_left),
.b_hi (b_right),
.intersect_vld (range_intersection_vld),
.partial_intersect (partial_intersect),
.complete_intersect (complete_intersect)
);
always_ff @ (posedge clk) begin
if (rst) begin
entirely_contained_count <= 0;
partially_contained_count <= 0;
end else if (range_intersection_vld) begin
entirely_contained_count <= entirely_contained_count + complete_intersect;
partially_contained_count <= partially_contained_count + partial_intersect;
end
end
always_ff @ (posedge clk) begin
counts_vld <= eof;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars(1, day_04);
end
endmodule
module demux_4 #
(
parameter WIDTH = 8,
)
(
input logic clk,
input logic vld,
input logic [WIDTH-1:0] data_in,
input logic [1:0] sel,
output logic [3:0] data_updated,
output logic [WIDTH*4-1:0] data_out
);
initial begin
data_out = 0;
data_updated = 0;
end
always_ff @ (posedge clk) begin
if (vld) begin
case (sel)
0: data_out <= {data_out[WIDTH*4-1 -: WIDTH*3], data_in};
1: data_out <= {data_out[WIDTH*4-1 -: WIDTH*2], data_in, data_out[WIDTH-1:0]};
2: data_out <= {data_out[WIDTH*4-1 -: WIDTH], data_in, data_out[WIDTH*2-1:0]};
3: data_out <= {data_in, data_out[WIDTH*3-1:0]};
endcase
end
end
always_ff @ (posedge clk) begin
case (sel)
0: data_updated <= {3'b000, vld};
1: data_updated <= {2'b00, vld, 1'b0};
2: data_updated <= {1'b0, vld, 2'b00};
3: data_updated <= {vld, 3'b000};
endcase
end
endmodule
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8
#!/usr/bin/env python3
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import Timer, RisingEdge
from cocotb.result import TestFailure, TestSuccess
@cocotb.test()
async def part_one(dut):
dut.clk.value = 0
dut.rst.value = 0
dut.vld.value = 0
dut.data.value = 0
dut.eof.value = 0
cocotb.start_soon(Clock(dut.clk, 4, units="ns").start())
for i in range(5):
await RisingEdge(dut.clk)
test_file = "actual.txt"
with open(test_file, "r") as f:
t = f.read()
for c in t:
await RisingEdge(dut.clk)
dut.vld.value = 1
dut.data.value = ord(c)
await RisingEdge(dut.clk)
dut.vld.value = 0
dut.data.value = 0
dut.eof.value = 1
await RisingEdge(dut.clk)
dut.vld.value = 0
dut.eof.value = 0
for i in range(5):
await RisingEdge(dut.clk)
partial_intersect_count = int(dut.partially_contained_count.value)
complete_intersect_count = int(dut.entirely_contained_count.value)
print("partial: ", partial_intersect_count)
print("complete: ", complete_intersect_count)
raise TestSuccess()
# Makefile
# defaults
SIM ?= icarus
TOPLEVEL_LANG ?= verilog
VERILOG_SOURCES += $(PWD)/../rtl/complete_day_04.v
# TOPLEVEL is the name of the toplevel module in your Verilog or VHDL files
TOPLEVEL = day_04
# Module is the basename of the Python test file
MODULE = main
# include cocotb's make rules to take care of the simulator setup
include $(shell cocotb-config --makefiles)/Makefile.sim
module range_intersection
(
input logic clk,
input logic vld,
input logic [7:0] a_lo,
input logic [7:0] a_hi,
input logic [7:0] b_lo,
input logic [7:0] b_hi,
output logic intersect_vld,
output logic partial_intersect,
output logic complete_intersect
);
initial begin
intersect_vld = 0;
end
// partially contains
logic a_within_b = 0;
logic b_within_a = 0;
// fully contains
logic a_contains_b = 0;
logic b_contains_a = 0;
always_ff @ (posedge clk) begin
a_within_b <= ((a_lo <= b_lo) && (a_hi >= b_lo)) ||
((a_lo <= b_hi) && (a_hi >= b_hi));
b_within_a <= ((b_lo <= a_lo) && (b_hi >= a_lo)) ||
((b_lo <= a_hi) && (b_hi >= a_hi));
end
always_ff @ (posedge clk) begin
a_contains_b <= (b_lo >= a_lo) & (a_hi >= b_hi);
b_contains_a <= (a_lo >= b_lo) & (b_hi >= a_hi);
end
always_ff @ (posedge clk) begin
intersect_vld <= vld;
end
always_comb begin
partial_intersect = a_within_b || b_within_a;
complete_intersect = a_contains_b || b_contains_a;
end
endmodule
module symbol_scanner
(
input logic vld,
input logic [7:0] text_byte,
output logic newline,
output logic comma,
output logic dash
);
logic comma_match;
logic newline_match;
logic dash_match;
constant_match # (.CONSTANT(44))
match_comma(
.data(text_byte),
.match(comma_match)
);
constant_match # (.CONSTANT(10))
match_newline (
.data(text_byte),
.match(newline_match)
);
constant_match # (.CONSTANT(45))
match_dash (
.data(text_byte),
.match(dash_match)
);
always_comb begin
newline = vld & newline_match;
comma = vld & comma_match;
dash = vld & dash_match;
end
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment