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.
1028 lines
29 KiB
Python
1028 lines
29 KiB
Python
2 years ago
|
# toysram sdr tb
|
||
|
# converted from pyverilator version
|
||
|
#
|
||
|
# variations:
|
||
|
# sdr: 1x
|
||
|
# ddr: 1x
|
||
|
# ddr: 2x
|
||
|
|
||
|
import cocotb
|
||
|
from cocotb.clock import Clock
|
||
|
from cocotb.triggers import Timer
|
||
|
from cocotb.triggers import FallingEdge
|
||
|
from cocotb.triggers import RisingEdge
|
||
|
from cocotb.handle import Force
|
||
|
from cocotb.handle import Release
|
||
|
|
||
|
import itertools
|
||
|
from dotmap import DotMap
|
||
|
from random import getrandbits
|
||
|
from random import randint
|
||
|
|
||
|
from OPEnv import *
|
||
|
|
||
|
def hexrandom(w=16):
|
||
|
n = getrandbits(w*4)
|
||
|
return '{0:0{l}X}'.format(n, l=w)
|
||
|
|
||
|
# Classes ----------------------------------------------------------------------------------------
|
||
|
|
||
|
class Memory:
|
||
|
|
||
|
def __init__(self, rows, bits, init=0):
|
||
|
self.mem = [init] * rows
|
||
|
self.rows = rows
|
||
|
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(self.rows, 4):
|
||
|
t1 = f'[{i:02X}] {self.mem[i]:0{bits/4}X}'
|
||
|
for j in range(i+1, i+4):
|
||
|
t1 += f' [{j:02X}] {self.mem[j]:0{bits/4}X}'
|
||
|
t += t1 + '\n'
|
||
|
return t
|
||
|
|
||
|
class Port:
|
||
|
|
||
|
def __init__(self, sim, id, type='r', nibbles=18):
|
||
|
self.sim = sim
|
||
|
self.dut = sim.dut
|
||
|
self.id = id
|
||
|
self.type = type
|
||
|
self.nibbles = nibbles
|
||
|
|
||
|
def read(self, adr):
|
||
|
if self.id == 0:
|
||
|
self.dut.rd_enb_0.value = 1
|
||
|
self.dut.rd_adr_0.value = adr
|
||
|
elif self.id == 1:
|
||
|
self.dut.rd_enb_1.value = 1
|
||
|
self.dut.rd_adr_1.value = adr
|
||
|
elif self.id == 2:
|
||
|
self.dut.rd_enb_2.value = 1
|
||
|
self.dut.rd_adr_2.value = adr
|
||
|
elif self.id == 3:
|
||
|
self.dut.rd_enb_3.value = 1
|
||
|
self.dut.rd_adr_3.value = adr
|
||
|
|
||
|
self.sim.msg(f'Port={self.id} RD @{adr:02X}')
|
||
|
|
||
|
def write(self, adr, dat):
|
||
|
if self.id == 0:
|
||
|
self.dut.wr_enb_0.value = 1
|
||
|
self.dut.wr_adr_0.value = adr
|
||
|
self.dut.wr_dat_0.value = dat
|
||
|
elif self.id == 1:
|
||
|
self.dut.wr_enb_1.value = 1
|
||
|
self.dut.wr_adr_1.value = adr
|
||
|
self.dut.wr_dat_1.value = dat
|
||
|
elif self.id == 2:
|
||
|
self.dut.wr_enb_2.value = 1
|
||
|
self.dut.wr_adr_2.value = adr
|
||
|
self.dut.wr_dat_2.value = dat
|
||
|
elif self.id == 3:
|
||
|
self.dut.wr_enb_3.value = 1
|
||
|
self.dut.wr_adr_3.value = adr
|
||
|
self.dut.wr_dat_3.value = dat
|
||
|
|
||
|
self.sim.msg(f'Port={self.id} WR @{adr:02X}={dat:0{self.nibbles}X}')
|
||
|
|
||
|
def data(self):
|
||
|
if self.id == 0:
|
||
|
return self.dut.rd_dat_0.value.integer
|
||
|
elif self.id == 1:
|
||
|
return self.dut.rd_dat_1.value.integer
|
||
|
elif self.id == 2:
|
||
|
return self.dut.rd_dat_2.value.integer
|
||
|
elif self.id == 3:
|
||
|
return self.dut.rd_dat_3.value.integer
|
||
|
|
||
|
def idle(self):
|
||
|
if self.type == 'r':
|
||
|
if self.id == 0:
|
||
|
self.dut.rd_enb_0.value = 0
|
||
|
self.dut.rd_adr_0.value = 0 # random
|
||
|
elif self.id == 1:
|
||
|
self.dut.rd_enb_1.value = 0
|
||
|
self.dut.rd_adr_1.value = 0 # random
|
||
|
elif self.id == 2:
|
||
|
self.dut.rd_enb_2.value = 0
|
||
|
self.dut.rd_adr_2.value = 0 # random
|
||
|
elif self.id == 3:
|
||
|
self.dut.rd_enb_3.value = 0
|
||
|
self.dut.rd_adr_3.value = 0 # random
|
||
|
else:
|
||
|
if self.id == 0:
|
||
|
self.dut.wr_enb_0.value = 0
|
||
|
self.dut.wr_adr_0.value = 0 # random
|
||
|
self.dut.wr_dat_0.value = 0 # random
|
||
|
elif self.id == 1:
|
||
|
self.dut.wr_enb_1.value = 0
|
||
|
self.dut.wr_adr_1.value = 0 # random
|
||
|
self.dut.wr_dat_1.value = 0 # random
|
||
|
elif self.id == 2:
|
||
|
self.dut.wr_enb_2.value = 0
|
||
|
self.dut.wr_adr_2.value = 0 # random
|
||
|
self.dut.wr_dat_2.value = 0 # random
|
||
|
elif self.id == 3:
|
||
|
self.dut.wr_enb_3.value = 0
|
||
|
self.dut.wr_adr_3.value = 0 # random
|
||
|
self.dut.wr_dat_31.value = 0 # random
|
||
|
|
||
|
def hex(n, pad=0):
|
||
|
if pad:
|
||
|
return f'000000000000000000000000{n.value.hex()[2:].upper()}'[-pad:]
|
||
|
else:
|
||
|
return n.value.hex()[2:].upper()
|
||
|
|
||
|
def printstate(sim):
|
||
|
if sim.sdr:
|
||
|
try:
|
||
|
sim.msg(f'R0: {sim.dut.ra.rd_enb_0_q.value} {hex(sim.dut.ra.rd_adr_0_q, 2)} {hex(sim.dut.ra.rd_dat_0_q, 18)} R1: {sim.dut.ra.rd_enb_1_q.value} {hex(sim.dut.ra.rd_adr_1_q, 2)} {hex(sim.dut.ra.rd_dat_1_q, 18)}')
|
||
|
except:
|
||
|
sim.msg(f'R0: {sim.dut.ra.rd_enb_0_q.value} {sim.dut.ra.rd_adr_0_q.value} {sim.dut.ra.rd_dat_0_q.value} R1: {sim.dut.ra.rd_enb_1_q.value} {sim.dut.ra.rd_adr_1_q.value} {sim.dut.ra.rd_dat_1_q.value}')
|
||
|
|
||
|
try:
|
||
|
sim.msg(f'W0: {sim.dut.ra.wr_enb_0_q.value} {hex(sim.dut.ra.wr_adr_0_q, 2)} {hex(sim.dut.ra.wr_dat_0_q, 18)}')
|
||
|
except:
|
||
|
sim.msg(f'W0: {sim.dut.ra.wr_enb_0_q.value} {sim.dut.ra.wr_adr_0_q.value} {sim.dut.ra.wr_dat_0_q.value}')
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
def printstate_32(sim):
|
||
|
if sim.sdr:
|
||
|
try:
|
||
|
sim.msg(f'R0: {sim.dut.ra.rd_enb_0_q.value} {hex(sim.dut.ra.rd_adr_0_q, 2)} {hex(sim.dut.ra.rd_dat_0_q, 8)} R1: {sim.dut.ra.rd_enb_1_q.value} {hex(sim.dut.ra.rd_adr_1_q, 2)} {hex(sim.dut.ra.rd_dat_1_q, 8)}')
|
||
|
except:
|
||
|
sim.msg(f'R0: {sim.dut.ra.rd_enb_0_q.value} {sim.dut.ra.rd_adr_0_q.value} {sim.dut.ra.rd_dat_0_q.value} R1: {sim.dut.ra.rd_enb_1_q.value} {sim.dut.ra.rd_adr_1_q.value} {sim.dut.ra.rd_dat_1_q.value}')
|
||
|
|
||
|
try:
|
||
|
sim.msg(f'W0: {sim.dut.ra.wr_enb_0_q.value} {hex(sim.dut.ra.wr_adr_0_q, 2)} {hex(sim.dut.ra.wr_dat_0_q, 8)}')
|
||
|
except:
|
||
|
sim.msg(f'W0: {sim.dut.ra.wr_enb_0_q.value} {sim.dut.ra.wr_adr_0_q.value} {sim.dut.ra.wr_dat_0_q.value}')
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
# Tasks
|
||
|
|
||
|
# get rid of z on anything that will be sampled here
|
||
|
# is there a func to get all inputs?
|
||
|
async def init(dut, sim):
|
||
|
"""Initialize inputs. """
|
||
|
|
||
|
return
|
||
|
|
||
|
async def initSite(dut, sim):
|
||
|
"""Initialize inputs. """
|
||
|
|
||
|
dut.wbs_stb_i.value = 0
|
||
|
dut.wbs_cyc_i.value = 0
|
||
|
dut.la_data_in.value = 0
|
||
|
dut.la_oenb.value = 0
|
||
|
dut.io_in.value = 0
|
||
|
|
||
|
return
|
||
|
|
||
|
async def config(dut, sim):
|
||
|
"""Configure core, etc. """
|
||
|
|
||
|
return
|
||
|
|
||
|
async def coreMonitor(dut, sim):
|
||
|
"""Watch for core events. """
|
||
|
|
||
|
return
|
||
|
|
||
|
async def genReset(dut, sim):
|
||
|
"""Generate reset. """
|
||
|
|
||
|
first = True
|
||
|
done = False
|
||
|
|
||
|
while not done:
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
if sim.cycle < sim.resetCycle:
|
||
|
if first:
|
||
|
sim.msg(f'Resetting...')
|
||
|
first = False
|
||
|
dut.reset.value = 1
|
||
|
elif not done:
|
||
|
sim.msg(f'Releasing reset.')
|
||
|
dut.reset.value = 0
|
||
|
done = True
|
||
|
|
||
|
async def genClocks(dut, sim):
|
||
|
"""Generate clock pulses. """
|
||
|
|
||
|
sim.clk1x = dut.clk
|
||
|
clk = Clock(sim.clk1x, sim.clk1xPeriod, 'ns')
|
||
|
await cocotb.start(clk.start())
|
||
|
|
||
|
sim.cycle = 0
|
||
|
while True:
|
||
|
sim.cycle += 1
|
||
|
if sim.cycle % sim.hbCycles == 0:
|
||
|
sim.msg(f'...tick...')
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
|
||
|
async def configSite(dut, sim):
|
||
|
"""Configure core, etc. """
|
||
|
|
||
|
return
|
||
|
|
||
|
async def genResetSite(dut, sim):
|
||
|
"""Generate reset. """
|
||
|
|
||
|
first = True
|
||
|
done = False
|
||
|
|
||
|
while not done:
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
if sim.cycle < sim.resetCycle:
|
||
|
if first:
|
||
|
sim.msg(f'Resetting...')
|
||
|
first = False
|
||
|
dut.wb_rst_i.value = 1
|
||
|
elif not done:
|
||
|
sim.msg(f'Releasing reset.')
|
||
|
dut.wb_rst_i.value = 0
|
||
|
done = True
|
||
|
|
||
|
async def genClocksSite(dut, sim):
|
||
|
"""Generate clock pulses. """
|
||
|
|
||
|
sim.clk1x = dut.wb_clk_i
|
||
|
clk = Clock(sim.clk1x, sim.clk1xPeriod, 'ns')
|
||
|
await cocotb.start(clk.start())
|
||
|
|
||
|
sim.cycle = 0
|
||
|
while True:
|
||
|
sim.cycle += 1
|
||
|
if sim.randomIO:
|
||
|
dut.io_in.value = int(hexrandom(9),16)*2 # randomize 1:36 (test_enable=0)
|
||
|
assert sim.cycle < sim.maxCycles, f'Maximum cycles reached!'
|
||
|
if sim.cycle % sim.hbCycles == 0:
|
||
|
sim.msg(f'...tick...')
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
# Interfaces
|
||
|
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
# Do something
|
||
|
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
@cocotb.test()
|
||
|
async def tb(dut):
|
||
|
"""ToySRAM 64x72 array test"""
|
||
|
|
||
|
sim = Sim(dut)
|
||
|
sim.sdr = True
|
||
|
sim.ddr = False
|
||
|
sim.clk1xPeriod = 1
|
||
|
sim.clk2x = False
|
||
|
sim.maxCycles = 50000
|
||
|
|
||
|
# init stuff
|
||
|
await init(dut, sim)
|
||
|
|
||
|
# start clocks,reset
|
||
|
clkGen = await cocotb.start(genClocks(dut, sim))
|
||
|
await cocotb.start(genReset(dut, sim))
|
||
|
|
||
|
await Timer(sim.resetCycle + 5, units='ns')
|
||
|
if dut.reset.value != 0:
|
||
|
sim.ok = False
|
||
|
sim.fail = 'Reset active too long!'
|
||
|
|
||
|
# config stuff
|
||
|
await config(dut, sim)
|
||
|
|
||
|
# monitor stuff
|
||
|
await cocotb.start(coreMonitor(dut, sim))
|
||
|
|
||
|
# do stuff
|
||
|
data = Memory(64, 72)
|
||
|
if sim.sdr:
|
||
|
portsRd = [Port(sim, 0, 'r'), Port(sim, 1, 'r')]
|
||
|
portsWr = [Port(sim, 0, 'w')]
|
||
|
else:
|
||
|
portsRd = [Port(sim, 0, 'r'), Port(sim, 1, 'r'), Port(sim, 2, 'r'), Port(sim, 3, 'r')]
|
||
|
portsWr = [Port(sim, 0, 'w'), Port(sim, 1, 'w')]
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
# idle
|
||
|
for p in portsRd:
|
||
|
p.idle()
|
||
|
for p in portsWr:
|
||
|
p.idle()
|
||
|
|
||
|
for i in range(10):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
# init array
|
||
|
sim.msg('Initializing array...')
|
||
|
if sim.sdr:
|
||
|
for a in range(64):
|
||
|
d0 = int(f'{a:02X}55555555555555{a:02X}', 16)
|
||
|
portsWr[0].write(a, d0)
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
data.write(a, d0) # now visible for reads
|
||
|
portsWr[0].idle()
|
||
|
else:
|
||
|
pass
|
||
|
'''
|
||
|
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
|
||
|
sim.msg('Running random commands...')
|
||
|
updates = []
|
||
|
checks = []
|
||
|
reads = [0, 0, 0, 0]
|
||
|
writes = [0, 0]
|
||
|
saveData = None
|
||
|
quiesced = False
|
||
|
quiesceCyc = sim.cycle + sim.maxCycles - 10
|
||
|
errors = 0
|
||
|
#rangesRd = [(0,63), (0,63), (0,63), (0,63)]
|
||
|
rangesRd = [(0,7), (0,7), (0,7), (0,7)]
|
||
|
#rangesWr = [(0,63), (0,63),(0,63), (0,63)]
|
||
|
rangesWr = [(0,7), (0,7)]
|
||
|
verbose = True
|
||
|
|
||
|
for c in range(sim.maxCycles):
|
||
|
|
||
|
sim.ok = True
|
||
|
|
||
|
await FallingEdge(sim.clk1x)
|
||
|
|
||
|
# check reads
|
||
|
checksNext = []
|
||
|
for i in range(len(checks)):
|
||
|
rd = checks[i]
|
||
|
if sim.cycle == rd[0]:
|
||
|
port = rd[1]
|
||
|
adr = rd[2]
|
||
|
act = portsRd[port].data()
|
||
|
exp = saveData[rd[2]]
|
||
|
if act != exp:
|
||
|
sim.ok = False
|
||
|
try:
|
||
|
sim.msg(f'* RD MISCOMPARE * port={port} adr={adr:02X} act={act:018X} exp={exp:018X}')
|
||
|
except:
|
||
|
sim.msg(f'* RD MISCOMPARE * port={port} adr={adr} act={act} exp={exp}')
|
||
|
elif verbose:
|
||
|
sim.msg(f'* RD COMPARE * port={port} adr={adr:02X} act={act:018X} exp={exp:018X}')
|
||
|
|
||
|
else:
|
||
|
checksNext.append(rd)
|
||
|
checks = checksNext
|
||
|
|
||
|
# do writes
|
||
|
updatesNext = [] # always only 1 cycle
|
||
|
for i in range(len(updates)):
|
||
|
wr = updates[i]
|
||
|
if sim.cycle == wr[0]:
|
||
|
data.write(wr[2], wr[3])
|
||
|
else:
|
||
|
assert False, f'HUH? should always be this cycle! {sim.cycle},{updates}'
|
||
|
updates = updatesNext
|
||
|
|
||
|
# save current data
|
||
|
saveData = data.readall()
|
||
|
|
||
|
# quiesce?
|
||
|
if sim.cycle >= quiesceCyc:
|
||
|
if not quiesced:
|
||
|
sim.msg('Quiescing...')
|
||
|
quiesced = True
|
||
|
|
||
|
# w0/w1 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((sim.cycle + 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((sim.cycle + 2, i, ar))
|
||
|
reads[i] += 1
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
#printstate(sim)
|
||
|
|
||
|
if not sim.ok: # and stopOnFail:
|
||
|
break
|
||
|
|
||
|
sim.msg('Done.')
|
||
|
# idle
|
||
|
for p in portsRd:
|
||
|
p.idle()
|
||
|
for p in portsWr:
|
||
|
p.idle()
|
||
|
|
||
|
sim.msg('Final State')
|
||
|
print(data)
|
||
|
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]}')
|
||
|
|
||
|
if sim.ok:
|
||
|
sim.msg(f'You has opulence.')
|
||
|
else:
|
||
|
for i in range(10):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
sim.msg(f'You are worthless and weak!')
|
||
|
assert False, f'[{sim.cycle:08d}] {sim.fail}'
|
||
|
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
@cocotb.test()
|
||
|
async def tb_32x32(dut):
|
||
|
"""ToySRAM 32x32 array test"""
|
||
|
|
||
|
sim = Sim(dut)
|
||
|
sim.sdr = True
|
||
|
sim.ddr = False
|
||
|
sim.clk1xPeriod = 1
|
||
|
sim.clk2x = False
|
||
|
sim.maxCycles = 10000
|
||
|
|
||
|
# init stuff
|
||
|
await init(dut, sim)
|
||
|
|
||
|
# start clocks,reset
|
||
|
clkGen = await cocotb.start(genClocks(dut, sim))
|
||
|
await cocotb.start(genReset(dut, sim))
|
||
|
|
||
|
await Timer(sim.resetCycle + 5, units='ns')
|
||
|
if dut.reset.value != 0:
|
||
|
sim.ok = False
|
||
|
sim.fail = 'Reset active too long!'
|
||
|
|
||
|
# config stuff
|
||
|
await config(dut, sim)
|
||
|
|
||
|
# monitor stuff
|
||
|
await cocotb.start(coreMonitor(dut, sim))
|
||
|
|
||
|
# do stuff
|
||
|
data = Memory(32, 32)
|
||
|
if sim.sdr:
|
||
|
portsRd = [Port(sim, 0, 'r', 8), Port(sim, 1, 'r', 8)]
|
||
|
portsWr = [Port(sim, 0, 'w', 8)]
|
||
|
else:
|
||
|
portsRd = [Port(sim, 0, 'r', 8), Port(sim, 1, 'r', 8), Port(sim, 2, 'r', 8), Port(sim, 3, 'r', 8)]
|
||
|
portsWr = [Port(sim, 0, 'w', 8), Port(sim, 1, 'w', 8)]
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
# idle
|
||
|
for p in portsRd:
|
||
|
p.idle()
|
||
|
for p in portsWr:
|
||
|
p.idle()
|
||
|
|
||
|
for i in range(10):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
# init array
|
||
|
sim.msg('Initializing array...')
|
||
|
if sim.sdr:
|
||
|
for a in range(32):
|
||
|
d0 = int(f'{a:02X}5555{a:02X}', 16)
|
||
|
portsWr[0].write(a, d0)
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
data.write(a, d0) # now visible for reads
|
||
|
portsWr[0].idle()
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
# random cmds
|
||
|
sim.msg('Running random commands...')
|
||
|
updates = []
|
||
|
checks = []
|
||
|
reads = [0, 0, 0, 0]
|
||
|
writes = [0, 0]
|
||
|
saveData = None
|
||
|
quiesced = False
|
||
|
quiesceCyc = sim.cycle + sim.maxCycles - 10
|
||
|
errors = 0
|
||
|
#rangesRd = [(0,1), (0,1), (0,1), (0,1)]
|
||
|
rangesRd = [(0,31), (0,31), (0,31), (0,31)]
|
||
|
#rangesWr = [(0,1), (0,1)]
|
||
|
rangesWr = [(0,31), (0,31)]
|
||
|
verbose = True
|
||
|
|
||
|
for c in range(sim.maxCycles):
|
||
|
|
||
|
sim.ok = True
|
||
|
|
||
|
await FallingEdge(sim.clk1x)
|
||
|
|
||
|
# check reads
|
||
|
checksNext = []
|
||
|
for i in range(len(checks)):
|
||
|
rd = checks[i]
|
||
|
if sim.cycle == rd[0]:
|
||
|
port = rd[1]
|
||
|
adr = rd[2]
|
||
|
act = portsRd[port].data()
|
||
|
exp = saveData[rd[2]]
|
||
|
if act != exp:
|
||
|
sim.ok = False
|
||
|
try:
|
||
|
sim.msg(f'* RD MISCOMPARE * port={port} adr={adr:02X} act={act:08X} exp={exp:08X}')
|
||
|
except:
|
||
|
sim.msg(f'* RD MISCOMPARE * port={port} adr={adr} act={act} exp={exp}')
|
||
|
elif verbose:
|
||
|
sim.msg(f'* RD COMPARE * port={port} adr={adr:02X} act={act:08X} exp={exp:08X}')
|
||
|
|
||
|
else:
|
||
|
checksNext.append(rd)
|
||
|
checks = checksNext
|
||
|
|
||
|
# do writes
|
||
|
updatesNext = [] # always only 1 cycle
|
||
|
for i in range(len(updates)):
|
||
|
wr = updates[i]
|
||
|
if sim.cycle == wr[0]:
|
||
|
data.write(wr[2], wr[3])
|
||
|
else:
|
||
|
assert False, f'HUH? should always be this cycle! {sim.cycle},{updates}'
|
||
|
updates = updatesNext
|
||
|
|
||
|
# save current data
|
||
|
saveData = data.readall()
|
||
|
|
||
|
# quiesce?
|
||
|
if sim.cycle >= quiesceCyc:
|
||
|
if not quiesced:
|
||
|
sim.msg('Quiescing...')
|
||
|
quiesced = True
|
||
|
|
||
|
# w0/w1 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(8), 16)
|
||
|
portsWr[i].write(aw[i], d)
|
||
|
updates.append((sim.cycle + 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((sim.cycle + 2, i, ar))
|
||
|
reads[i] += 1
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
#printstate_32(sim)
|
||
|
|
||
|
if not sim.ok: # and stopOnFail:
|
||
|
break
|
||
|
|
||
|
sim.msg('Done.')
|
||
|
# idle
|
||
|
for p in portsRd:
|
||
|
p.idle()
|
||
|
for p in portsWr:
|
||
|
p.idle()
|
||
|
|
||
|
sim.msg('Final State')
|
||
|
print(data)
|
||
|
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]}')
|
||
|
|
||
|
if sim.ok:
|
||
|
sim.msg(f'You has opulence.')
|
||
|
else:
|
||
|
for i in range(10):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
sim.msg(f'You are worthless and weak!')
|
||
|
assert False, f'[{sim.cycle:08d}] {sim.fail}'
|
||
|
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
# ------------------------------------------------------------------------------------------------
|
||
|
@cocotb.test()
|
||
|
async def tb_site(dut):
|
||
|
"""ToySRAM site test"""
|
||
|
|
||
|
sim = Sim(dut)
|
||
|
sim.sdr = True
|
||
|
sim.ddr = False
|
||
|
sim.clk1xPeriod = 1
|
||
|
sim.clk2x = False
|
||
|
sim.maxCycles = 50000
|
||
|
sim.randomIO = True
|
||
|
|
||
|
cfgBase = 0x00000000
|
||
|
ctlBase = 0x00010000
|
||
|
ra0Base = 0x00100000
|
||
|
|
||
|
# init stuff
|
||
|
await initSite(dut, sim)
|
||
|
|
||
|
# start clocks,reset
|
||
|
clkGen = await cocotb.start(genClocksSite(dut, sim))
|
||
|
await cocotb.start(genResetSite(dut, sim))
|
||
|
|
||
|
await Timer(sim.resetCycle + 5, units='ns')
|
||
|
if dut.wb_rst_i.value != 0:
|
||
|
sim.ok = False
|
||
|
sim.fail = 'Reset active too long!'
|
||
|
|
||
|
if dut.io_in.value != 0:
|
||
|
sim.ok = False
|
||
|
sim.fail = 'I/O inputs not zero!'
|
||
|
|
||
|
# config stuff
|
||
|
await configSite(dut, sim)
|
||
|
|
||
|
# monitor stuff
|
||
|
await cocotb.start(coreMonitor(dut, sim))
|
||
|
|
||
|
# do stuff
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
ra = Memory(32, 32)
|
||
|
|
||
|
# write and read ra0 w0/r0
|
||
|
for i in range(32):
|
||
|
|
||
|
adr = ra0Base + i
|
||
|
dat = int(hexrandom(8), 16)
|
||
|
sim.msg(f'Writing Port 0 @{adr:08X} {i:02X}={dat:08X}')
|
||
|
dut.wbs_cyc_i.value = 1
|
||
|
dut.wbs_stb_i.value = 1
|
||
|
dut.wbs_we_i.value = 1
|
||
|
dut.wbs_adr_i.value = adr
|
||
|
dut.wbs_sel_i.value = 0xF
|
||
|
dut.wbs_dat_i.value = dat
|
||
|
ra.write(i, dat)
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
dut.wbs_cyc_i.value = 0
|
||
|
dut.wbs_stb_i.value = 0
|
||
|
|
||
|
while (dut.wbs_ack_o.value == 0):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
sim.msg(f'Reading Port 0 @{adr:08X} {i:02X}')
|
||
|
dut.wbs_cyc_i.value = 1
|
||
|
dut.wbs_stb_i.value = 1
|
||
|
dut.wbs_we_i.value = 0
|
||
|
dut.wbs_adr_i.value = adr
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
dut.wbs_cyc_i.value = 0
|
||
|
dut.wbs_stb_i.value = 0
|
||
|
|
||
|
while (dut.wbs_ack_o.value == 0):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
sim.msg(f'Read Data: {dut.wbs_dat_o.value.integer:08X}')
|
||
|
assert dut.wbs_dat_o.value == dat, f'Read data miscompare! exp={dat:08X} act={dut.wbs_dat_o.value.integer:08X}'
|
||
|
|
||
|
# read ra0 r1
|
||
|
for i in range(32):
|
||
|
|
||
|
adr = ra0Base + 0x00004000 + i
|
||
|
sim.msg(f'Reading Port 1 @{adr:08X} {i:02X}')
|
||
|
dut.wbs_cyc_i.value = 1
|
||
|
dut.wbs_stb_i.value = 1
|
||
|
dut.wbs_we_i.value = 0
|
||
|
dut.wbs_adr_i.value = adr
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
dut.wbs_cyc_i.value = 0
|
||
|
dut.wbs_stb_i.value = 0
|
||
|
|
||
|
while (dut.wbs_ack_o.value == 0):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
sim.msg(f'Read Data: {dut.wbs_dat_o.value.integer:08X}')
|
||
|
assert dut.wbs_dat_o.value == ra.read(i), f'Read data miscompare! exp={ra.read(i):08X} act={dut.wbs_dat_o.value.integer:08X}'
|
||
|
|
||
|
# read ra0 r0,r1 through scan
|
||
|
|
||
|
# control.v
|
||
|
test_enable = dut.io_in[0];
|
||
|
scan_clk = dut.io_in[1];
|
||
|
scan_di = dut.io_in[2];
|
||
|
scan_do = dut.io_out[3]
|
||
|
|
||
|
io_ra0_clk = dut.io_in[4];
|
||
|
io_ra0_rst = dut.io_in[5];
|
||
|
io_ra0_r0_enb = dut.io_in[6];
|
||
|
io_ra0_r1_enb = dut.io_in[7];
|
||
|
io_ra0_w0_enb = dut.io_in[8];
|
||
|
|
||
|
'''
|
||
|
io_ra0_r0_adr = scan_reg_q[127:123];
|
||
|
io_ra0_r0_dat = scan_reg_q[122:91]; // loaded by io_ra0_clk
|
||
|
io_ra0_r1_adr = scan_reg_q[90:86];
|
||
|
io_ra0_r1_dat = scan_reg_q[85:54]; // loaded by io_ra0_clk
|
||
|
io_ra0_w0_adr = scan_reg_q[53:49];
|
||
|
io_ra0_w0_dat = scan_reg_q[48:17];
|
||
|
'''
|
||
|
|
||
|
scanLen = 128
|
||
|
scanHalfPer = 1
|
||
|
noisy = False
|
||
|
|
||
|
sim.msg(f'Starting scan sequences.')
|
||
|
test_enable.value = 1
|
||
|
sim.randomIO = False
|
||
|
|
||
|
# load 0's
|
||
|
sim.msg(f'Writing zeroes...')
|
||
|
scan_di.value = 0
|
||
|
for i in range(scanLen):
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
|
||
|
# check 0's
|
||
|
sim.msg(f'Checking zeroes...')
|
||
|
assert scan_do.value == 0, f'Scanout not 0! {scan_do.value}'
|
||
|
for i in range(1, scanLen):
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
assert scan_do.value == 0, f'Scanout not 0! {scan_do.value}'
|
||
|
|
||
|
# read r0,r1
|
||
|
sim.msg(f'Reading R0@15, R1@16...')
|
||
|
scanIn = '01111' + '11111111111111111111111111111111' + \
|
||
|
'10000' + '11111111111111111111111111111111' + \
|
||
|
'00000' + '00000000000000000000000000000000' + \
|
||
|
'00000000000000000'
|
||
|
|
||
|
sim.msg(f'Scanning in...')
|
||
|
for i in range(scanLen):
|
||
|
scan_di.value = int(scanIn[i])
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
|
||
|
readHalfPer = 50
|
||
|
|
||
|
io_ra0_rst.value = 0
|
||
|
io_ra0_r0_enb.value = 1
|
||
|
io_ra0_r1_enb.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
# adr
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
# dat
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_r0_enb.value = 0
|
||
|
io_ra0_r1_enb.value = 0
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
# capture in scan_reg
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
|
||
|
sim.msg(f'Scanning out...')
|
||
|
scan_di.value = 0
|
||
|
scanOut = f'{scan_do.value}'
|
||
|
for i in range(1, scanLen):
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scanOut += f'{scan_do.value}'
|
||
|
|
||
|
sim.msg(f'ScanData={int(scanOut,2):032X}')
|
||
|
sim.msg(f' r0 adr:{int(scanOut[0:5], 2):02X}')
|
||
|
sim.msg(f' r0 dat:{int(scanOut[5:37], 2):08X}')
|
||
|
assert int(scanOut[5:37], 2) == ra.read(15), f'R0 Miscompare! exp={ra.read(15):08X} dat={int(scanOut[5:37], 2):08X}'
|
||
|
sim.msg(f' r1 adr:{int(scanOut[37:42], 2):02X}')
|
||
|
sim.msg(f' r1 dat:{int(scanOut[42:74], 2):08X}')
|
||
|
assert int(scanOut[42:74], 2) == ra.read(16), f'R0 Miscompare! exp={ra.read(16):08X} dat={int(scanOut[42:74], 2):08X}'
|
||
|
sim.msg(f' w0 adr:{int(scanOut[74:79], 2 ):02X}')
|
||
|
sim.msg(f' w0 dat:{int(scanOut[79:111], 2):08X}')
|
||
|
sim.msg(f' cfg:{int(scanOut[111:], 2):05X}')
|
||
|
|
||
|
# write w0
|
||
|
sim.msg(f'Writing W0@15=08675309...')
|
||
|
scanIn = '00000' + '11111111111111111111111111111111' + \
|
||
|
'00000' + '11111111111111111111111111111111' + \
|
||
|
'01111' + '00001000011001110101001100001001' + \
|
||
|
'00000000000000000'
|
||
|
|
||
|
sim.msg(f'Scanning in...')
|
||
|
for i in range(scanLen):
|
||
|
scan_di.value = int(scanIn[i])
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
|
||
|
readHalfPer = 50
|
||
|
|
||
|
io_ra0_rst.value = 0
|
||
|
io_ra0_w0_enb.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
# adr/dat
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
io_ra0_w0_enb.value = 0
|
||
|
ra.write(15, int('08675309', 16))
|
||
|
|
||
|
# read r0,r1
|
||
|
sim.msg(f'Reading R0@15, R1@16...')
|
||
|
scanIn = '01111' + '11111111111111111111111111111111' + \
|
||
|
'10000' + '11111111111111111111111111111111' + \
|
||
|
'00000' + '00000000000000000000000000000000' + \
|
||
|
'00000000000000000'
|
||
|
|
||
|
sim.msg(f'Scanning in...')
|
||
|
for i in range(scanLen):
|
||
|
scan_di.value = int(scanIn[i])
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
|
||
|
readHalfPer = 50
|
||
|
|
||
|
io_ra0_rst.value = 0
|
||
|
io_ra0_r0_enb.value = 1
|
||
|
io_ra0_r1_enb.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
# adr
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
if noisy:
|
||
|
sim.msg(f' ScanReg={dut.site.ctl.scan_reg_q.value}')
|
||
|
# dat
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_r0_enb.value = 0
|
||
|
io_ra0_r1_enb.value = 0
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
# capture in scan_reg
|
||
|
sim.msg('Blipping RA0 clk...')
|
||
|
io_ra0_clk.value = 1
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
io_ra0_clk.value = 0
|
||
|
await Timer(readHalfPer, units='ns')
|
||
|
|
||
|
sim.msg(f'Scanning out...')
|
||
|
scan_di.value = 0
|
||
|
scanOut = f'{scan_do.value}'
|
||
|
for i in range(1, scanLen):
|
||
|
scan_clk.value = 0
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scan_clk.value = 1
|
||
|
await Timer(scanHalfPer, units='ns')
|
||
|
scanOut += f'{scan_do.value}'
|
||
|
|
||
|
sim.msg(f'ScanData={int(scanOut,2):032X}')
|
||
|
sim.msg(f' r0 adr:{int(scanOut[0:5], 2):02X}')
|
||
|
sim.msg(f' r0 dat:{int(scanOut[5:37], 2):08X}')
|
||
|
assert int(scanOut[5:37], 2) == ra.read(15), f'R0 Miscompare! exp={ra.read(15):08X} dat={int(scanOut[5:37], 2):08X}'
|
||
|
sim.msg(f' r1 adr:{int(scanOut[37:42], 2):02X}')
|
||
|
sim.msg(f' r1 dat:{int(scanOut[42:74], 2):08X}')
|
||
|
assert int(scanOut[42:74], 2) == ra.read(16), f'R0 Miscompare! exp={ra.read(16):08X} dat={int(scanOut[42:74], 2):08X}'
|
||
|
sim.msg(f' w0 adr:{int(scanOut[74:79], 2 ):02X}')
|
||
|
sim.msg(f' w0 dat:{int(scanOut[79:111], 2):08X}')
|
||
|
sim.msg(f' cfg:{int(scanOut[111:], 2):05X}')
|
||
|
|
||
|
test_enable.value = 0
|
||
|
sim.randomIO = True
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
sim.msg(f'Starting regular sequences.')
|
||
|
|
||
|
# write and read ra0 w0/r0
|
||
|
for i in range(32):
|
||
|
|
||
|
adr = ra0Base + i
|
||
|
dat = int(hexrandom(8), 16)
|
||
|
sim.msg(f'Writing Port 0 @{adr:08X} {i:02X}={dat:08X}')
|
||
|
dut.wbs_cyc_i.value = 1
|
||
|
dut.wbs_stb_i.value = 1
|
||
|
dut.wbs_we_i.value = 1
|
||
|
dut.wbs_adr_i.value = adr
|
||
|
dut.wbs_sel_i.value = 0xF
|
||
|
dut.wbs_dat_i.value = dat
|
||
|
ra.write(i, dat)
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
dut.wbs_cyc_i.value = 0
|
||
|
dut.wbs_stb_i.value = 0
|
||
|
|
||
|
while (dut.wbs_ack_o.value == 0):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
sim.msg(f'Reading Port 0 @{adr:08X} {i:02X}')
|
||
|
dut.wbs_cyc_i.value = 1
|
||
|
dut.wbs_stb_i.value = 1
|
||
|
dut.wbs_we_i.value = 0
|
||
|
dut.wbs_adr_i.value = adr
|
||
|
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
dut.wbs_cyc_i.value = 0
|
||
|
dut.wbs_stb_i.value = 0
|
||
|
|
||
|
while (dut.wbs_ack_o.value == 0):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|
||
|
sim.msg(f'Read Data: {dut.wbs_dat_o.value.integer:08X}')
|
||
|
assert dut.wbs_dat_o.value == dat, f'Read data miscompare! exp={dat:08X} act={dut.wbs_dat_o.value.integer:08X}'
|
||
|
|
||
|
sim.msg('Done')
|
||
|
|
||
|
for i in range(10):
|
||
|
await RisingEdge(sim.clk1x)
|
||
|
|