You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

445 lines
11 KiB
Python

#!/usr/bin/python3
# pyverilator
# fixed internal sig parsing (cdata/wdata)
# 1. this should be based on init setting AND should be done even w/o trace on!!!
# in add_to_vcd_trace(self), time is bumped +5
# 2. should count cycs
# 3. add parm so clock can be set but NOT eval (for multiclock, only fastest evals)
# 4. how to access mem[][]??
# 5. not adding vectors to gtk - cuz 0:n?
import os, sys
import datetime
from optparse import OptionParser
from optparse import OptionGroup
import random
from random import randint
from pysutils import *
user = os.environ['USER']
binPath = os.path.dirname(os.path.realpath(__file__))
localPV = True
if localPV:
import os, sys
sys.path.append(os.path.join(binPath, 'pyverilator'))
import pyverilator
####################################################################
# Defaults
rtl = ['src']
model = 'sdr'
stopOnFail = True
verbose = False
vcd = False
seed = randint(1, int('8675309', 16))
runCycs = 100
#rangesRd = [(0,63), (0,63), (0,63), (0,63)]
rangesRd = [(0,7), (0,7), (0,7), (0,7)]
#rangesWr = [(0,63), (0,63)]
rangesWr = [(0,7), (0,7)]
####################################################################
# Process command line
usage = "Usage: %prog [options]"
parser = OptionParser(usage)
parser.add_option('-m', '--model', dest='model', help=f'sdr or ddr')
parser.add_option('-s', '--seed', dest='seed', help=f'initialize seed to n')
parser.add_option('-c', '--cycles', dest='runCycs', help=f'cycles to run, default={runCycs}')
parser.add_option('-t', '--trace', dest='trace', action='store_true', help=f'create wave file')
parser.add_option('-f', '--stopfail', dest='stopOnFail', action='store_true', help=f'stop on first fail')
parser.add_option('-v', '--verbose', dest='verbose', action='store_true', help=f'noisy output')
options, args = parser.parse_args()
if options.model is not None:
model = options.model
if options.seed is not None:
seed = int(options.seed)
if options.runCycs is not None:
runCycs = int(options.runCycs)
if options.trace is not None:
vcd = True
if options.stopOnFail is not None:
stopOnFail = True
if options.verbose is not None:
verbose = True
####################################################################
# Init
sdr = False
ddr = False
ddr1x = False
if model == 'sdr':
top = 'test_ra_sdr.v'
sdr = True
elif model == 'ddr1x':
top = 'test_ra_ddr_1x.v'
ddr = True
ddr1x = True
else:
top = 'test_ra_ddr.v'
ddr = True
errors = 0
cyc = 0
quiesceCyc = 5 # before end
# build model
sim = pyverilator.PyVerilator.build(top, verilog_path=rtl)
print('io')
print(sim.io)
print()
print('internals')
# issue #8 - try local fix
print(sim.internals)
print()
#print('ra')
#print(sim.internals.ra)
# array0,1,2 dont exist as submodules???
#print()
#
#print('ra.add_clk')
#print(sim.internals.ra.add_clk)
#print()
if vcd:
sim.start_gtkwave(auto_tracing=False)
#wtf vectors are failing
# will make this load a savefile anyway someday
# this doesn't actually restrict what's beign recorded anyway; still
# can load saved netlist after sim
#sim.send_to_gtkwave(sim.io)
#for s in sim.io:
# try:
# sim.send_to_gtkwave(sim.io[s])
# except:
# print(f'*** failed {s}')
####################################################################
# Functions, Classes
def getSimTime():
return (sim.curr_time, cyc)
msg(init=getSimTime)
# sim-driven signals don't look like _q since they are set after the eval(clk=1) tick
# would have to set after eval of rising edge but also not do a simtick
def tick():
sim.eval()
if vcd:
sim.add_to_vcd_trace()
def run(n=1, cb=None):
global cyc
if sdr or ddr1x:
for i in range(n):
sim.io.clk = 0
tick()
sim.io.clk = 1
tick()
elif ddr:
for i in range(n):
sim.io.clk = 0
sim.io.clk2x = 1
tick()
sim.io.clk2x = 0
tick()
sim.io.clk = 1
sim.io.clk2x = 1
tick()
sim.io.clk2x = 0
tick()
cyc += 1
if not vcd: # should be done by pyv!!!!
sim.curr_time = cyc * 10
if cb is not None:
(cb)()
def fail(t=None):
global errors, stopOnFail
msg('*** FAIL ***')
errors += 1
if t is not None:
msg(t)
class Memory:
def __init__(self, locs, bits, init=0):
self.mem = [init] * locs
self.bits = bits
def read(self, adr):
return self.mem[adr]
def readall(self):
mem = []
for i in range(len(self.mem)):
mem.append(self.mem[i])
return mem
def write(self, adr, dat):
self.mem[adr] = dat
def __str__(self):
t = ''
for i in range(0,len(self.mem),4):
t1 = f'[{i:02X}] {self.mem[i]:018X}'
for j in range(i+1, i+4):
t1 += f' [{j:02X}] {self.mem[j]:018X}'
#t1 += f' {self.mem[j]:018X}\n'
t += t1 + '\n'
return t
class Port:
def __init__(self, id, type='r'):
self.id = id
self.type = type
def read(self, adr):
sim.io[f'rd_enb_{self.id}'] = 1
sim.io[f'rd_adr_{self.id}'] = adr
msg(f'Port={self.id} RD @{adr:02X}')
def write(self, adr, dat):
sim.io[f'wr_enb_{self.id}'] = 1
sim.io[f'wr_adr_{self.id}'] = adr
sim.io[f'wr_dat_{self.id}'] = dat
msg(f'Port={self.id} WR @{adr:02X}={dat:02X}')
def data(self):
return int(sim.io[f'rd_dat_{self.id}'])
def idle(self):
if self.type == 'r':
sim.io[f'rd_enb_{self.id}'] = 0
sim.io[f'rd_adr_{self.id}'] = 0 # random
else:
sim.io[f'wr_enb_{self.id}'] = 0
sim.io[f'wr_adr_{self.id}'] = 0 # random
sim.io[f'wr_dat_{self.id}'] = 0 # random
def printstate():
mac = sim.internals.ra
if sdr:
msg(f'R0: {mac.rd_enb_0_q:01X} {mac.rd_adr_0_q:02X} {mac.rd_dat_0_q:018X} R1: {mac.rd_enb_1_q:01X} {mac.rd_adr_1_q:02X} {mac.rd_dat_1_q:018X}')
msg(f'W0: {mac.wr_enb_0_q:01X} {mac.wr_adr_0_q:02X} {mac.wr_dat_0_q:018X}')
else:
msg(f'R0: {mac.rd_enb_0_q:01X} {mac.rd_adr_0_q:02X} {mac.rd_dat_0_q:018X} R1: {mac.rd_enb_1_q:01X} {mac.rd_adr_1_q:02X} {mac.rd_dat_1_q:018X} R2: {mac.rd_enb_2_q:01X} {mac.rd_adr_2_q:02X} {mac.rd_dat_2_q:018X} R3: {mac.rd_enb_3_q:01X} {mac.rd_adr_3_q:02X} {mac.rd_dat_3_q:018X}')
msg(f'W0: {mac.wr_enb_0_q:01X} {mac.wr_adr_0_q:02X} {mac.wr_dat_0_q:018X} W1: {mac.wr_enb_1_q:01X} {mac.wr_adr_1_q:02X} {mac.wr_dat_1_q:018X}')
def printfinal():
print()
print()
print('Final State')
print(f'Model : {top}')
print()
print(data)
# should be checking actual mem[][] here, but can't access signals
print()
for i in range(len(portsRd)):
print(f'Reads Port {i}: {reads[i]}')
for i in range(len(portsWr)):
print(f'Writes Port {i}: {writes[i]}')
print()
print(f'Seed: {seed:08X}')
print(f'Cycles: {cyc}')
print(f'Errors: {errors}')
def check(port, adr, exp=None):
if exp is None:
exp = data.read(adr)
act = portsRd[port].data()
if act != exp:
fail(f'* RD MISCOMPARE * port={port} adr={adr:02X} act={act:018X} exp={exp:018X}')
return False
elif verbose:
msg(f'* RD COMPARE * port={port} adr={adr:02X} act={act:018X} exp={exp:018X}')
return True
####################################################################
# Do something
msg(f'Initializing seed to {hex(seed)}')
random.seed(seed)
data = Memory(64, 72)
if sdr:
portsRd = [Port(0, 'r'), Port(1, 'r')]
portsWr = [Port(0, 'w')]
else:
portsRd = [Port(0, 'r'), Port(1, 'r'), Port(2, 'r'), Port(3, 'r')]
portsWr = [Port(0, 'w'), Port(1, 'w')]
# Array Cycle Timings
#
# write
# | e/a/d | acc | valid |
# * latched by wrapper (in)
#
#
# read
# | e/a | acc | valid |
# * latched by wrapper (in)
# * latched by wrapper (out)
#
# rd(a) = wr(a) (both enabled):
# reset
sim.io.reset = 1
run(1)
sim.io.reset = 0
# idle
for p in portsRd:
p.idle()
for p in portsWr:
p.idle()
run(10)
# init array
if sdr:
for a in range(0, 64, 1):
d0 = int(f'5555555555555555{a:02X}', 16)
portsWr[0].write(a, d0)
run(1, printstate)
data.write(a, d0) # now visible for reads
portsWr[0].idle()
else:
for a in range(0, 64, 2):
d0 = int(f'5555555555555555{a:02X}', 16)
portsWr[0].write(a, d0)
d1 = int(f'5555555555555555{a+1:02X}', 16)
portsWr[1].write(a+1, d1)
run(1, printstate)
data.write(a, d0) # now visible for reads
data.write(a+1, d1) # now visible for reads
portsWr[0].idle()
portsWr[1].idle()
# random cmds
# writes: visible to all reads in cycle n+1,...
# reads: check in cycle n+2 vs mem data in cycle n+1
#
# every cycle:
# save data state
# pick weighted read0, read1, read2, read3, write0, write1 (cmd freq, adr) and ensure no adr coll if req'd
# schedule data change (write)
# schedule checks (read)
updates = []
checks = []
reads = [0, 0, 0, 0]
writes = [0, 0]
saveData = None
quiesced = False
quiesceCyc = cyc + runCycs - quiesceCyc
#d = int('1000', 16)
msg('Starting random loop.')
for c in range(runCycs):
ok = True
# check reads
checksNext = []
for i in range(len(checks)):
rd = checks[i]
if cyc == rd[0]:
ok = ok and check(rd[1], rd[2], saveData[rd[2]])
else:
checksNext.append(rd)
checks = checksNext
# do writes
updatesNext = [] # always only 1 cycle
for i in range(len(updates)):
wr = updates[i]
if cyc == wr[0]:
data.write(wr[2], wr[3])
else:
print('HUH? should always be this cycle!', cyc, updates)
quit()
updates = updatesNext
# save current data
saveData = data.readall()
# quiesce?
if cyc >= quiesceCyc:
if not quiesced:
msg('Quiescing...')
quiesced = True
# write coll will give w1 precedence - or make it avoid
aw = [None] * 2
for i in range(len(portsWr)):
portsWr[i].idle()
aw[i] = -1
if not quiesced and randint(1, 10) < 5:
r = rangesWr[i]
aw[i] = randint(r[0], r[1])
d = int(hexrandom(18), 16)
portsWr[i].write(aw[i], d)
updates.append((cyc+1, i, aw[i], d))
writes[i] += 1
for i in range(len(portsRd)):
portsRd[i].idle()
if not quiesced and randint(1, 10) < 5:
r = rangesRd[i]
ar = randint(r[0], r[1])
while ar == aw[0] or ar == aw[1]:
ar = randint(r[0], r[1])
portsRd[i].read(ar)
checks.append((cyc+2, i, ar))
reads[i] += 1
run(1, printstate)
if not ok and stopOnFail:
break
####################################################################
# Clean up
printfinal()
if ok and errors == 0:
print()
print('You has opulence.')
print()
else:
print()
print('You are worthless and weak!')
print()
print('Done.')