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.

415 lines
16 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# A2L2 Interface
import cocotb
from cocotb.triggers import Timer, RisingEdge
from cocotb.binary import BinaryValue
from cocotb.handle import Force
from cocotb.handle import Release
from dotmap import DotMap
import itertools
# ------------------------------------------------------------------------------------------------
# Classes
'''
Data return timings from spec:
* Reload Data Coming
Indicates that reload data is coming in three cycles. This signal is required for L2 designs that return data in consecutive cycles, but can be
tied to a logic 0 for designs that return data in every other cycle. For L2 designs that return data in consecutive cycles, this signal should be
asserted three cycles ahead of the first of two paired data beats. If more than two data beats will be presented consecutively, this signal should be
asserted once for the first set of two (paired) data beats and once for the second set of two (paired) data beats. Each assertion
should be three cycles ahead of the first beat of the paired set of data beats. This signal allows the A2 core to insert an issue bubble for the second
reload data beat to avoid flushing the processor pipe. This signal is not required to be asserted as described above for DITC return data.
For non-cacheable (I=1) reloads of one or two beats, this signal should be asserted three cycles ahead of the first (and possibly only) data beat transfer.
* Reload Data Valid
Indicates that reload data is coming in two cycles. This signal qualifies the other reload interface signals sent in this cycle: reld_ditc, reld_core_tag, reld_crit_qw, and reld_qw.
If reld_data_vld is not active, then the qualified signals should be ignored.
* Reload Direct Inter-Thread Communication
Indicates that the reload data is associated with a DITC transfer instead of a standard load-ttype reload. This signal is qualified by reld_data_vld and determines the
interpretation of the reld_core_tag bus. DITC reload data transfers are always 64-Byte transfers that follow the same consecutive cycle or every-other-cycle behavior
as standard load-ttype reloads for the attached L2.
**i believe this means ditc can use either 1of2/2of2 or 1of2/-/-/2of2 pattern, but never requires data_coming (probs because pipe considerations not important for ditc)**
======
Cycles:
* d-3 (reld_data_coming)
Loads:
1. I=1: assert 3 cycs ahead of first transfer (two transfers only if 32B allowed)
2. I=0 data every other cycle: not asserted
3. I=0 data consecutive cycles: assert 3 cycs ahead of the 1of2 paired beats; if more than 2 beats are consecutive, assert 3 cycs ahead of each paired beat
DITC:
1. assertion not required **(or used by core?)**
* d-2 (reld_data_vld and qualified signals)
Loads:
1. assert 2 cycs ahead of data
DITC:
1. assert 2 cycs ahead of data and also assert reld_ditc
Cacheable Return Modes:
1. no back-to-back: coming=0
2. all back-to-back: coming=1/0/1
3. interleaved back-to-back: coming=1/0/0/0/1
4. mixed: legal cases for subxfers (?) **i think the 'mixed' aren't valid - xucr0[52] selects b2b mode**
* 1 1 1 1 (no b2b)
* 1 2 1 (mixed)
* 1 1 2 (mixed)
* 2 1 1 (mixed)
* 2 2 (full b2b)
5. between subxfers a delay or other transaction can be inserted
?? xucr0[52] definition selects b2b but also says crit first; i guess this means crit first is allowed, but not required?? a2l2 spec says it is not required to send crit first
'''
class A2L2Trans(DotMap):
'''A2L2 Transaction'''
nextID = itertools.count()
def __init__(self, sim, tid, tt, tag=None, addr=None, length=0, wimg=0, cycD=None, be=None, data=None):
super().__init__()
self.sim = sim
self.id = next(A2L2Trans.nextID)
self.tid = tid
self.tt = tt
self.tag = tag
self.addr = addr
self.len = length
self.wimg = wimg
self.xfers = 1
self.xferNum = 0
self.xferCrit = 1
self.beatNum = 1
if cycD is not None:
self.cycC = cycD - 3
self.cycV = cycD - 2
self.cycD = cycD
self.be = f'{int(be, 16):032b}' if be is not None else None
self.data = data
self.done = False
self.ieq1 = wimg & 0x4 == 0x4
self.load = tt == 0x00 or tt == 0x08 or tt == 0x22 or tt == 0x09 or tt == 0x0B # IF, LD, DITC, LARX, LARX_HINT
self.store = tt == 0x20 or tt == 0x29 # ST, STCX
if self.load:
self.addr = self.addr & 0xFFFFFFF0
elif self.store:
self.addr = self.addr & 0xFFFFFFE0
if self.be == None or self.data == None:
raise Exception('A2L2Trans: store must have BE and data')
else:
self.len = 0
self.storeStart = None
for i in range(len(self.be)):
if self.be[i] == '1':
if self.storeStart is None:
self.storeStart = i
self.len += 1
elif self.storeStart is not None:
break
else:
raise Exception(f'A2L2Trans: unsupported ttype={tt}')
self.ditc = tt == 0x22
if self.ieq1:
if tt == 0x00 or tt == 0x08: # IF, LD
if len == 7:
self.xfers = 2
elif tt == 0x22: # DITC
self.xfers = 4
else:
if self.load:
self.xfers = 4
self.xferCrit = ((self.addr & 0x30) >> 4) + 1
self.addr = self.addr & 0xFFFFFFC0
def readXfer(self):
# read() returns this qw crit-first if cacheable!
w0 = self.sim.mem.read(self.addr)
w1 = self.sim.mem.read(self.addr+4)
w2 = self.sim.mem.read(self.addr+8)
w3 = self.sim.mem.read(self.addr+12)
beatNum = self.beatNum
if self.beatNum < self.xfers:
self.beatNum += 1
self.cycD += 1
self.addr += 16 #wtf this is wrong - going to need to schedule the pattern when the trans is created!!!!!!!!!!!!!!!!!!!!!!!!
return w0,w1,w2,w3,beatNum
def doStore(self):
addr = ((self.addr + self.storeStart) >> 2) << 2
dataStart = self.storeStart*2
if self.len == 1:
word = self.sim.mem.read(addr)
byte = self.addr & 0x3
if byte == 0:
mask = 0xFFFFFF00
elif byte == 1:
mask = 0xFFFF00FF
elif byte == 2:
mask = 0xFF00FFFF
else:
mask = 0x00FFFFFF
word = (word & mask) | (int(self.data[dataStart:dataStart+2], 16) << (byte*2))
self.sim.mem.write(addr, word)
elif self.len == 2:
word = self.sim.mem.read(addr)
hw = (self.addr & 0x2) >> 1
if hw == 0:
mask = 0xFFFF0000
else:
mask = 0x0000FFFF
word = (word & mask) | (int(self.data[dataStart:dataStart+4], 16) << (hw*4))
self.sim.mem.write(addr, word)
elif self.len == 4:
self.sim.mem.write(addr, int(self.data[dataStart:dataStart+8], 16))
elif self.len == 8:
self.sim.mem.write(addr, int(self.data[dataStart:dataStart+16], 16))
self.sim.mem.write(addr+4, int(self.data[dataStart+16:dataStart+32], 16))
elif self.len == 16:
self.sim.mem.write(addr, int(self.data[0:8], 16))
self.sim.mem.write(addr+4, int(self.data[8:16], 16))
self.sim.mem.write(addr+8, int(self.data[16:24], 16))
self.sim.mem.write(addr+12, int(self.data[24:32], 16))
elif self.len == 32:
self.sim.mem.write(addr, int(self.data[0:8], 16))
self.sim.mem.write(addr+4, int(self.data[8:16], 16))
self.sim.mem.write(addr+8, int(self.data[16:24], 16))
self.sim.mem.write(addr+12, int(self.data[24:32], 16))
self.sim.mem.write(addr+16, int(self.data[32:40], 16))
self.sim.mem.write(addr+20, int(self.data[40:48], 16))
self.sim.mem.write(addr+24, int(self.data[48:56], 16))
self.sim.mem.write(addr+28, int(self.data[56:64], 16))
else:
raise Exception(f'A2L2Trans: unsupported store len={self.len}')
# ------------------------------------------------------------------------------------------------
# Functions
def hex(n, pad=0):
if pad:
return f'000000000000000000000000{n.value.hex()[2:].upper()}'[-pad:]
else:
return n.value.hex()[2:].upper()
# ------------------------------------------------------------------------------------------------
# Tasks
async def A2L2Driver(dut, sim):
"""A2L2 node interface"""
transTypes = {
'00': 'IFETCH',
'08': 'LOAD',
'20': 'STORE'
}
ok = True
readPending = []
countReads = 0
mem = {}
sim.msg('A2L2 Driver: started.')
dut.an_ac_flh2l2_gate.value = 0
while ok and not sim.done:
await RisingEdge(dut.clk_1x)
dut.an_ac_req_ld_pop.value = 0
dut.an_ac_req_st_pop.value = 0
dut.an_ac_req_st_gather.value = 0
dut.an_ac_reld_data_coming.value = 0
dut.an_ac_reld_data_vld.value = 0
dut.an_ac_reld_ecc_err.value = 0
dut.an_ac_reld_ecc_err_ue.value = 0
dut.an_ac_reld_ditc.value = 0
dut.an_ac_reld_l1_dump.value = 0
dut.an_ac_req_spare_ctrl_a1.value = 0
if sim.threads == 1:
dut.an_ac_reservation_vld.value = 0
dut.an_ac_stcx_complete.value = 0
dut.an_ac_stcx_pass.value = 0
else:
for i in range(sim.threads):
dut.an_ac_reservation_vld[i].value = 0
dut.an_ac_stcx_complete[i].value = 0
dut.an_ac_stcx_pass[i].value = 0
dut.an_ac_sync_ack.value = 0
dut.an_ac_icbi_ack.value = 0
dut.an_ac_back_inv.value = 0
# bummer IndexError: Slice indexing is not supported
#dut.an_ac_reld_data[0:31].value = 0x48000000
#dut.an_ac_reld_data[32:63].value = 0x48000000
#dut.an_ac_reld_data[64:95].value = 0x48000000
#dut.an_ac_reld_data[96:127].value = 0x48000000
# bummer TypeError: Unsupported type for value assignment: <class 'str'> ('48000000480000004800000048000000')
#dut.an_ac_reld_data.value = '48000000480000004800000048000000'
#v = 0x48000000
# bummer TypeError: Unsupported type for value assignment: <class 'str'> ('01001000000000000000000000000000010010000000000000000000000000000100100000000000000000000000000001001000000000000000000000000000')
#dut.an_ac_reld_data.value = f'{v:0>32b}{v:0>32b}{v:0>32b}{v:0>32b}'
# otay!
#v1 = cocotb.binary.BinaryValue()
#v1.assign(f'{v:0>32b}{v:0>32b}{v:0>32b}{v:0>32b}')
#dut.an_ac_reld_data.value = v1.value
if dut.ac_an_req.value: # should first check ac_an_req_pwr_token prev cyc
tt = hex(dut.ac_an_req_ttype, 2)
transType = transTypes[tt]
tid = hex(dut.ac_an_req_thread)
ra = hex(dut.ac_an_req_ra, 8)
tag = hex(dut.ac_an_req_ld_core_tag, 2)
lenEnc = hex(dut.ac_an_req_ld_xfr_len)
le = 'LE ' if dut.ac_an_req_endian.value else ''
wimg_w = dut.ac_an_req_wimg_w.value
wimg_i = dut.ac_an_req_wimg_i.value
wimg_m = dut.ac_an_req_wimg_m.value
wimg_g = dut.ac_an_req_wimg_g.value
wimg = 0
if wimg_w:
wimg += 8
if wimg_i:
wimg += 4
if wimg_m:
wimg += 2
if wimg_g:
wimg += 1
if transType == 'IFETCH' or transType == 'LOAD':
# when allowing out-of-order, schedule reld once added
if len(readPending) == 0:
reldCyc = sim.cycle + 6 # const for now
else:
reldCyc = readPending[-1].cycD + 4 # worst-case const for now
trans = A2L2Trans(sim, tid, int(tt, 16), int(tag, 16), int(ra, 16), int(lenEnc, 16), wimg, reldCyc)
readPending.append(trans)
sim.msg(f'T{tid} {transType} {ra} tag={tag} len={trans.len} {le}WIMG:{wimg:X} reld data:{trans.cycD}')
elif transType == 'STORE':
# should verify st_data_pwr_token prev cycle
be = hex(dut.ac_an_st_byte_enbl, 8)
data = hex(dut.ac_an_st_data, 64)
trans = A2L2Trans(sim, tid, int(tt, 16), int(tag, 16), int(ra, 16), int(lenEnc, 16), wimg, None, be=be, data=data)
sim.msg(f'T{tid} {transType} {ra} tag={tag} len={trans.len} be={be} data={data} {le}WIMG:{wimg:X}')
trans.doStore()
dut.an_ac_req_st_pop.value = 1 #wtf can randomize, etc.
#assert False, 'got a store'
# data early indicator (d-3)
dut.an_ac_reld_data_coming.value = 0
for i in range(len(readPending)):
trans = readPending[i]
if trans.cycC == sim.cycle:
dut.an_ac_reld_data_coming.value = 1
if trans.xferNum == 0 and trans.xfers == 4: # 4 beats b2b - need diff scheduling for all modes
trans.cycC += 2
# data valid indicator (d-2)
dut.an_ac_reld_data_vld.value = 0
dut.an_ac_reld_core_tag.value = 0x1F
dut.an_ac_reld_ditc.value = 1
dut.an_ac_reld_qw.value = 3
dut.an_ac_reld_crit_qw.value = 1
for i in range(len(readPending)):
trans = readPending[i]
if trans.cycV == sim.cycle:
trans.xferNum += 1
dut.an_ac_reld_data_vld.value = 1
dut.an_ac_reld_core_tag.value = trans.tag
dut.an_ac_reld_ditc.value = 1 if trans.ditc else 0
dut.an_ac_reld_qw.value = trans.xferNum - 1
dut.an_ac_reld_crit_qw.value = 1 if trans.xferNum == trans.xferCrit else 0
if trans.xferNum < 4 and trans.xfers == 4:
trans.cycV += 1
# data beat
if len(readPending) > 0 and readPending[0].cycD == sim.cycle: # ordered
trans = readPending[0]
w0,w1,w2,w3,beatNum = trans.readXfer()
v1 = cocotb.binary.BinaryValue()
v1.assign(f'{w0:0>32b}{w1:0>32b}{w2:0>32b}{w3:0>32b}')
dut.an_ac_reld_data.value = v1.value
sim.msg(f'RELD tag={trans.tag:02X} {w0:08X}{w1:08X}{w2:08X}{w3:08X} {beatNum}of{trans.xfers}{" crit" if beatNum == trans.xferCrit else ""}')
if beatNum == trans.xfers:
trans.done = True
countReads += 1 #wtf do this in monitor
if len(readPending) == 1:
readPending = []
else:
readPending = readPending[1:]
dut.an_ac_req_ld_pop.value = 1 #wtf can randomize, etc.
# A2L2 Checker
# check protocol, etc.
async def A2L2Checker(dut, sim):
"""A2L2 interface checker """
me = 'A2L2 Checker'
ok = True
sim.msg(f'{me}: started.')
while ok:
await RisingEdge(dut.clk_1x)
# A2L2 Monitor
# count transactions, etc.
# fail on bad addresses
async def A2L2Monitor(dut, sim):
"""A2L2 interface monitor"""
me = 'A2L2 Monitor'
ok = True
start = len(sim.config.a2l2.badAddr) > 0
sim.msg(f'{me}: started.')
while start and ok:
await RisingEdge(dut.clk_1x)
if dut.ac_an_req.value: # should first check ac_an_req_pwr_token prev cyc
tt = hex(dut.ac_an_req_ttype, 2)
if tt == '00': #wtf someone should make this a enum/class
ra = dut.ac_an_req_ra.value.integer
for i in range(len(sim.config.a2l2.badAddr)):
blk = sim.config.a2l2.badAddr[i]
if 'I' in blk[2].upper():
blkStart = int(blk[0], 16)
blkEnd = int(blk[1], 16)
if ra >= blkStart and ra <= blkEnd:
ok = False
assert False, (f'{me}: Bad IFetch @={ra:08X}') #wtf want this to end back in main code for summary
class A2L2:
driver = A2L2Driver
checker = A2L2Checker
monitor = A2L2Monitor
def __init__(self):
pass