add prototype simulation replay

This commit is contained in:
Fischer Moseley 2023-04-15 17:05:37 -04:00
parent 1c74d4a714
commit 9d8836bda3
6 changed files with 4266 additions and 56 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,4 +1,14 @@
from manta import Manta
import pickle
m = Manta('manta.yaml')
print(m.my_logic_analyzer.run())
# capture = m.my_logic_analyzer.capture()
# with open("capture.pkl", "wb") as f:
# pickle.dump(capture, f)
with open("capture.pkl", "rb") as f:
capture = pickle.load(f)
m.my_logic_analyzer.export_vcd(capture, "capture.vcd")
m.my_logic_analyzer.export_mem(capture, "capture.mem")

View File

@ -0,0 +1,80 @@
`default_nettype none
`timescale 1ns/1ps
module replay_tb();
logic clk;
always begin
#5;
clk = !clk;
end
logic larry;
logic curly;
logic moe;
logic [3:0] shemp;
my_logic_analyzer_playback playback (
.clk(clk),
.enable(1'b1),
.done(),
.larry(larry),
.curly(curly),
.moe(moe),
.shemp(shemp));
initial begin
clk = 0;
$dumpfile("replay_tb.vcd");
$dumpvars(0, replay_tb);
#(4500*5);
$finish();
end
endmodule
module my_logic_analyzer_playback (
input wire clk,
input wire enable,
output reg done,
output reg larry,
output reg curly,
output reg moe,
output reg [3:0] shemp);
localparam FILENAME = "capture.mem";
localparam SAMPLE_DEPTH = 4096;
localparam TOTAL_PROBE_WIDTH = 7;
reg [TOTAL_PROBE_WIDTH-1:0] capture [SAMPLE_DEPTH-1:0];
reg [$clog2(SAMPLE_DEPTH)-1:0] addr;
reg [TOTAL_PROBE_WIDTH-1:0] sample;
assign done = (addr >= SAMPLE_DEPTH);
initial begin
$display("Loading capture from %s", FILENAME);
$readmemb(FILENAME, capture);
addr = 0;
end
always @(posedge clk) begin
if (enable && !done) begin
addr = addr + 1;
sample = capture[addr];
larry = sample[0];
curly = sample[1];
moe = sample[2];
shemp = sample[6:3];
end
end
endmodule
`default_nettype wire

View File

@ -14,7 +14,7 @@ module top_level (
logic [3:0] shemp = 0;
always_ff @(posedge clk) begin
{larry, curly, moe, shemp} <= {larry, curly, moe, shemp} + 1;
{shemp, moe, curly, larry} <= {shemp, moe, curly, larry} + 1;
end
manta manta_inst (

View File

@ -551,7 +551,15 @@ class LogicAnalyzerCore:
la.sub(self.total_probe_width, "/* TOTAL_PROBE_WIDTH */")
probes_concat = '{' + ', '.join(self.probes.keys()) + '}'
# concatenate the probes together to make one big register,
# but do so such that the first probe in the config file
# is at the least-significant position in that big register.
#
# this makes part-selecting out from the memory easier to
# implement in python, and because verilog and python conventions
# are different, we would have had to reverse it somwehere anyway
probes_concat = list(self.probes.keys())[::-1]
probes_concat = '{' + ', '.join(probes_concat) + '}'
la.sub(probes_concat, "/* PROBES_CONCAT */")
return la.get_hdl()
@ -584,8 +592,10 @@ class LogicAnalyzerCore:
# functions for actually using the core:
def run(self):
pass
def capture(self):
# Check state - if it's in anything other than IDLE,
# request to stop the existing capture
print(" -> Resetting core...")
@ -631,71 +641,85 @@ class LogicAnalyzerCore:
return block_mem_contents[read_pointer:] + block_mem_contents[:read_pointer]
def part_select(self, data, width):
top, bottom = width
assert top >= bottom
mask = 2 ** (top - bottom + 1) - 1
return (data >> bottom) & mask
def make_widths(self, config):
# {probe0, probe1, probe2}
# [12, 1, 3] should produce
# [ (15, 4) (3, 3) (2,0) ]
widths = list(config["downlink"]["probes"].values())
# easiest to make by summing them and incrementally subtracting
s = sum(widths)
slices = []
for width in widths:
slices.append((s - 1, s - width))
s = s - width
assert s == 0, "Probe sizes are weird, cannot slice bits properly"
return slices
def export_waveform(self, config, data, path):
extension = path.split(".")[-1]
assert extension == "vcd", "Unrecognized waveform export format."
def export_vcd(self, capture_data, path):
from vcd import VCDWriter
vcd_file = open(path, "w")
# Use the datetime format that iVerilog uses
# Use the same datetime format that iVerilog uses
timestamp = datetime.now().strftime("%a %b %w %H:%M:%S %Y")
with VCDWriter(
vcd_file, timescale="10 ns", date=timestamp, version="manta"
) as writer:
# add probes to vcd file
vcd_probes = []
for name, width in config["downlink"]["probes"].items():
probe = writer.register_var("manta", name, "wire", size=width)
vcd_probes.append(probe)
with VCDWriter(vcd_file, '10 ns', timestamp, "manta") as writer:
# each probe has a name, width, and writer associated with it
signals = []
for name, width in self.probes.items():
signal = {
"name" : name,
"width" : width,
"data" : self.part_select_capture_data(capture_data, name),
"var": writer.register_var("manta", name, "wire", size=width)
}
signals.append(signal)
# add clock to vcd file
clock = writer.register_var("manta", "clk", "wire", size=1)
# calculate bit widths for part selecting
widths = self.make_widths(config)
# add the data to each probe in the vcd file
for timestamp in range(0, 2*len(capture_data)):
# slice data, and dump to vcd file
for timestamp in range(2 * len(data)):
value = data[timestamp // 2]
# dump clock values to vcd file
# note: this assumes logic is triggered
# on the rising edge of the clock, @TODO fix this
# run the clock
writer.change(clock, timestamp, timestamp % 2 == 0)
for probe_num, probe in enumerate(vcd_probes):
val = self.part_select(value, widths[probe_num])
writer.change(probe, timestamp, val)
# add other signals
for signal in signals:
var = signal["var"]
sample = signal["data"][timestamp // 2]
writer.change(var, timestamp, sample)
vcd_file.close()
def export_mem(self, capture_data, path):
with open(path, "w") as f:
# a wee bit of cursed string formatting, but just
# outputs each sample as binary, padded to a fixed length
w = self.total_probe_width
f.writelines([f'{s:0{w}b}\n' for s in capture_data])
def export_mem_loader(self):
pass
def part_select_capture_data(self, capture_data, probe_name):
"""Given the name of the probe, part-select the appropriate bits of capture data,
and return as an integer. Accepts capture_data as an integer or a list of integers."""
# sum up the widths of the probes below this one
lower = 0
for name, width in self.probes.items():
if name == probe_name:
break
lower += width
upper = lower + (self.probes[probe_name] - 1)
# define the part select
mask = 2 ** (upper - lower + 1) - 1
part_select = lambda x: (x >> lower) & mask
# apply the part_select function depending on type
if isinstance(capture_data, int):
return part_select(capture_data)
elif isinstance(capture_data, list):
for i in capture_data:
assert isinstance(i, int), "Can only part select on integers and list of integers."
return [part_select(sample) for sample in capture_data]
else:
raise ValueError("Can only part select on integers and lists of integers.")
class BlockMemoryCore:
def __init__(self, config, name, base_addr, interface):
self.name = name