# 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 from OPEnv import * # ------------------------------------------------------------------------------------------------ # 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, le=False): super().__init__() self.sim = sim self.id = next(A2L2Trans.nextID) self.tid = tid self.tt = tt self.tag = tag self.addr = addr if length == 0 or length == 3: raise Exception(f'A2L2Trans: bad length encode: {length}') elif length == 5: self.len = 8 elif length == 6: self.len = 16 elif length == 7: self.len = 32 else: 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.LE = le 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 #wtf definitely 16B-aligned occurring #self.addr = self.addr & 0xFFFFFFF0 # keep low bits for 1B and 2B stores 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 & 0xFFFFFFF0) + self.storeStart) >> 2) << 2 # word-align dataStart = self.storeStart*2 if self.len == 1: word = self.sim.mem.read(addr) byte = self.addr & 0x3 if self.LE: 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*8)) else: if byte == 0: mask = 0x00FFFFFF elif byte == 1: mask = 0xFF00FFFF elif byte == 2: mask = 0xFFFF00FF else: mask = 0xFFFFFF00 word = (word & mask) | (int(self.data[dataStart:dataStart+2], 16) << ((3-byte)*8)) self.sim.mem.write(addr, word) elif self.len == 2: word = self.sim.mem.read(addr) hw = (self.addr & 0x2) >> 1 if self.LE: if hw == 0: mask = 0xFFFF0000 else: mask = 0x0000FFFF word = (word & mask) | (int(self.data[dataStart:dataStart+4], 16) << (hw*16)) else: if hw == 0: mask = 0x0000FFFF else: mask = 0xFFFF0000 word = (word & mask) | (int(self.data[dataStart:dataStart+4], 16) << ((1-hw)*16)) 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: if self.LE: 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)) else: self.sim.mem.write(addr+4, int(self.data[dataStart:dataStart+16], 16)) self.sim.mem.write(addr, int(self.data[dataStart+16:dataStart+32], 16)) elif self.len == 16: if self.LE: 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)) else: self.sim.mem.write(addr+12, int(self.data[0:8], 16)) self.sim.mem.write(addr+8, int(self.data[8:16], 16)) self.sim.mem.write(addr+4, int(self.data[16:24], 16)) self.sim.mem.write(addr, int(self.data[24:32], 16)) elif self.len == 32: if self.LE: 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: self.sim.mem.write(addr+28, int(self.data[0:8], 16)) self.sim.mem.write(addr+24, int(self.data[8:16], 16)) self.sim.mem.write(addr+20, int(self.data[16:24], 16)) self.sim.mem.write(addr+16, int(self.data[24:32], 16)) self.sim.mem.write(addr+12, int(self.data[32:40], 16)) self.sim.mem.write(addr+8, int(self.data[40:48], 16)) self.sim.mem.write(addr+4, int(self.data[48:56], 16)) self.sim.mem.write(addr, int(self.data[56:64], 16)) else: raise Exception(f'A2L2Trans: unsupported store len={self.len}') # ------------------------------------------------------------------------------------------------ # Tasks transTypes = { '00': 'IFETCH', '08': 'LOAD', '20': 'STORE' } async def A2L2Driver(dut, sim): """A2L2 node interface""" ok = True readPending = [] countReads = 0 mem = {} sim.msg('A2L2 Driver: started.') sim.a2o.root.an_ac_flh2l2_gate.value = 0 while ok and not sim.done: await RisingEdge(dut.clk_1x) sim.a2o.root.an_ac_req_ld_pop.value = 0 sim.a2o.root.an_ac_req_st_pop.value = 0 sim.a2o.root.an_ac_req_st_gather.value = 0 sim.a2o.root.an_ac_reld_data_coming.value = 0 sim.a2o.root.an_ac_reld_data_vld.value = 0 sim.a2o.root.an_ac_reld_ecc_err.value = 0 sim.a2o.root.an_ac_reld_ecc_err_ue.value = 0 sim.a2o.root.an_ac_reld_ditc.value = 0 sim.a2o.root.an_ac_reld_l1_dump.value = 0 sim.a2o.root.an_ac_req_spare_ctrl_a1.value = 0 if sim.threads == 1: sim.a2o.root.an_ac_reservation_vld.value = 0 sim.a2o.root.an_ac_stcx_complete.value = 0 sim.a2o.root.an_ac_stcx_pass.value = 0 else: for i in range(sim.threads): sim.a2o.root.an_ac_reservation_vld[i].value = 0 sim.a2o.root.an_ac_stcx_complete[i].value = 0 sim.a2o.root.an_ac_stcx_pass[i].value = 0 sim.a2o.root.an_ac_sync_ack.value = 0 sim.a2o.root.an_ac_icbi_ack.value = 0 sim.a2o.root.an_ac_back_inv.value = 0 # bummer IndexError: Slice indexing is not supported #sim.a2o.root.an_ac_reld_data[0:31].value = 0x48000000 #sim.a2o.root.an_ac_reld_data[32:63].value = 0x48000000 #sim.a2o.root.an_ac_reld_data[64:95].value = 0x48000000 #sim.a2o.root.an_ac_reld_data[96:127].value = 0x48000000 # bummer TypeError: Unsupported type for value assignment: ('48000000480000004800000048000000') #sim.a2o.root.an_ac_reld_data.value = '48000000480000004800000048000000' #v = 0x48000000 # bummer TypeError: Unsupported type for value assignment: ('01001000000000000000000000000000010010000000000000000000000000000100100000000000000000000000000001001000000000000000000000000000') #sim.a2o.root.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}') #sim.a2o.root.an_ac_reld_data.value = v1.value if sim.a2o.root.ac_an_req.value: # should first check ac_an_req_pwr_token prev cyc tt = hex(sim.a2o.root.ac_an_req_ttype, 2) transType = transTypes[tt] tid = hex(sim.a2o.root.ac_an_req_thread) ra = hex(sim.a2o.root.ac_an_req_ra, 8) tag = hex(sim.a2o.root.ac_an_req_ld_core_tag, 2) lenEnc = hex(sim.a2o.root.ac_an_req_ld_xfr_len) le = 'LE ' if sim.a2o.root.ac_an_req_endian.value else '' wimg_w = sim.a2o.root.ac_an_req_wimg_w.value wimg_i = sim.a2o.root.ac_an_req_wimg_i.value wimg_m = sim.a2o.root.ac_an_req_wimg_m.value wimg_g = sim.a2o.root.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, le=le) 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(sim.a2o.root.ac_an_st_byte_enbl, 8) data = hex(sim.a2o.root.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, le=le) sim.msg(f'T{tid} {transType} {ra} tag={tag} len={trans.len} be={be} data={data} {le}WIMG:{wimg:X}') trans.doStore() sim.a2o.root.an_ac_req_st_pop.value = 1 #wtf can randomize, etc. #assert False, 'got a store' # data early indicator (d-3) sim.a2o.root.an_ac_reld_data_coming.value = 0 for i in range(len(readPending)): trans = readPending[i] if trans.cycC == sim.cycle: sim.a2o.root.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) sim.a2o.root.an_ac_reld_data_vld.value = 0 sim.a2o.root.an_ac_reld_core_tag.value = 0x1F sim.a2o.root.an_ac_reld_ditc.value = 1 sim.a2o.root.an_ac_reld_qw.value = 3 sim.a2o.root.an_ac_reld_crit_qw.value = 1 for i in range(len(readPending)): trans = readPending[i] if trans.cycV == sim.cycle: trans.xferNum += 1 sim.a2o.root.an_ac_reld_data_vld.value = 1 sim.a2o.root.an_ac_reld_core_tag.value = trans.tag sim.a2o.root.an_ac_reld_ditc.value = 1 if trans.ditc else 0 sim.a2o.root.an_ac_reld_qw.value = trans.xferNum - 1 sim.a2o.root.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}') sim.a2o.root.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:] sim.a2o.root.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 # TRANS MONITORING NOT COMPLETE! async def A2L2Monitor(dut, sim, watchTrans=False): """A2L2 interface monitor""" me = 'A2L2 Monitor' ok = True start = len(sim.config.a2l2.badAddr) > 0 sim.msg(f'{me}: started.') reqValid = [] rldValidCyc = [] dataValidCyc = [] while start and ok: await RisingEdge(dut.clk_1x) if sim.a2o.root.ac_an_req.value: # should first check ac_an_req_pwr_token prev cyc tt = hex(sim.a2o.root.ac_an_req_ttype, 2) transType = transTypes[tt] tid = hex(sim.a2o.root.ac_an_req_thread) ra = hex(sim.a2o.root.ac_an_req_ra, 8) tag = hex(sim.a2o.root.ac_an_req_ld_core_tag, 2) lenEnc = hex(sim.a2o.root.ac_an_req_ld_xfr_len) le = 'LE ' if sim.a2o.root.ac_an_req_endian.value else '' wimg_w = sim.a2o.root.ac_an_req_wimg_w.value wimg_i = sim.a2o.root.ac_an_req_wimg_i.value wimg_m = sim.a2o.root.ac_an_req_wimg_m.value wimg_g = sim.a2o.root.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': sim.msg(f'T{tid} {transType} {ra} tag={tag} len={lenEnc} {le}WIMG:{wimg:X}') trans = A2L2Trans(sim, tid, int(tt, 16), int(tag, 16), int(ra, 16), int(lenEnc, 16), wimg, None, le=le) reqValid.append(trans) elif transType == 'STORE': be = hex(sim.a2o.root.ac_an_st_byte_enbl, 8) data = hex(sim.a2o.root.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, le=le) sim.msg(f'T{tid} {transType} {ra} tag={tag} len={lenEnc} be={be} data={data} {le}WIMG:{wimg:X}') if tt == '00': #wtf someone should make this a enum/class ra = sim.a2o.root.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}') # coming (d-3_) if sim.a2o.root.an_ac_reld_data_coming.value: rldValidCyc.append(sim.cycle + 1) # data valid indicator (d-2) if len(rldValidCyc) > 0 and rldValidCyc[0] == sim.cycle: if sim.a2o.root.an_ac_reld_data_vld.value: #wtf append obj to rldValid! tag = sim.a2o.root.an_ac_reld_core_tag.value ditc = sim.a2o.root.an_ac_reld_ditc.value qw = sim.a2o.root.an_ac_reld_qw.value crit = sim.a2o.root.an_ac_reld_crit_qw.value rldValidCyc = rldValidCyc[1:] dataValidCyc.append(sim.cycle + 2) else: assert False, (f'{me}: Missing valid cycle') # data beat (d-0) if len(dataValidCyc) > 0 and dataValidCyc[0] == sim.cycle: data = hex(sim.a2o.root.an_ac_reld_data, 32) sim.msg(f'RELD tag={tag:02X} {data:32X}') #wtf need qw,crit dataValidCyc = dataValidCyc[1:] class A2L2: driver = A2L2Driver checker = A2L2Checker monitor = A2L2Monitor def __init__(self): pass