def connect_update_port(self, update: AxiCacheTagArrayUpdateIntf, tag_mem_port_w: BramPort_withoutClk): update_tmp = self._reg("update_tmp", HStruct( (update.addr._dtype, "addr"), (BIT, "delete"), (update.way_en._dtype, "way_en"), (BIT, "vld"), ), def_val={"vld": 0}) update_tmp(update) tag, index, _ = self.parse_addr(update.addr) tag_mem_port_w.en(update.vld) tag_mem_port_w.addr(index) # construct the byte enable mask for various tag enable configurations # prepare write tag in every way but byte enable only requested ways tag_record = self.tag_record_t.from_py({ "tag": tag, "valid": ~update.delete, }) tag_record = tag_record._reinterpret_cast( Bits(self.tag_record_t.bit_length())) tag_mem_port_w.din(Concat(*(tag_record for _ in range(self.WAY_CNT)))) tag_be_t = Bits(self.tag_record_t.bit_length() // 8) tag_en = tag_be_t.from_py(tag_be_t.all_mask()) tag_not_en = tag_be_t.from_py(0) tag_mem_port_w.we( Concat(*reversed( [en._ternary(tag_en, tag_not_en) for en in update.way_en]))) return update_tmp
def test_BitsFromIncompatibleType(self): t = Bits(2) with self.assertRaises(ValueError): t.from_py("a1") with self.assertRaises(TypeError): t.from_py(object())
def setConfig(self, crcConfigCls): """ Apply configuration from CRC configuration class """ word_t = Bits(crcConfigCls.WIDTH) self.POLY = word_t.from_py(crcConfigCls.POLY) self.POLY_WIDTH = crcConfigCls.WIDTH self.REFIN = crcConfigCls.REFIN self.REFOUT = crcConfigCls.REFOUT self.XOROUT = word_t.from_py(crcConfigCls.XOROUT) self.INIT = word_t.from_py(crcConfigCls.INIT)
def shiftIntArray(values: List[Union[int, BitsVal]], item_width: int, shift: int): """ :param values: array of values which will be shifted as a whole :param item_width: a bit length of a single item in array :param shift: specifies how many bits the array should be shifted, << is a positive shift, >> is a negative shift """ if shift == 0: return copy(values) new_v = [] t = Bits(item_width) if shift > 0: # << for _ in range(shift // item_width): new_v.append(None) prev = None for v in values: if v is None and prev is None: _v = None else: if prev is None: prev = t.from_py(None) if v is None: v = t.from_py(None) elif isinstance(prev, BitsVal) and not isinstance(v, BitsVal): v = t.from_py(v) _v = (v << shift) | (prev >> (item_width - shift)) new_v.append(_v) prev = v if prev is None: v = None else: v = prev >> (item_width - shift) new_v.append(v) else: # shift < 0, >> nextIt = iter(values) for v in values: try: nv = next(nextIt) except StopIteration: nv = None if nv is None and v is None: _v = None else: if nv is None: nv = t.from_py(None) if v is None: v = t.from_py(None) _v = (v >> shift) | (nv & mask(shift)) new_v.append(_v) return new_v
def test_BitsFromPyEnum(self): class PyEnumCls(Enum): A = 1 B = 3 C = 4 t = Bits(2) self.assertValEq(t.from_py(PyEnumCls.A), 1) self.assertValEq(t.from_py(PyEnumCls.B), 3) with self.assertRaises(ValueError): t.from_py(PyEnumCls.C)
def test_array_eq_neq(self): t = Bits(8)[5] v0 = t.from_py(range(5)) v1 = t.from_py({0: 10, 1: 2}) v2 = t.from_py([1, 2, 3, 4, 5]) self.assertTrue(v0._eq(v0)) with self.assertRaises(ValueError): self.assertNotEqual(v0, v1) self.assertNotEqual(v0, v2) with self.assertRaises(ValueError): self.assertNotEqual(v1, v2) with self.assertRaises(ValueError): self.assertNotEqual(v1, v1) self.assertTrue(v2, v2)
def _as_Bits(self, val: Union[RtlSignal, Value]): if val._dtype == BOOL: bit1_t = Bits(1) o = self.createTmpVarFn("tmpBool2std_logic_", bit1_t) ifTrue, ifFalse = bit1_t.from_py(1), bit1_t.from_py(0) if_ = If(val) if_.ifTrue.append(Assignment(ifTrue, o, virtual_only=True, parentStm=if_)) if_.ifFalse = [] if_.ifFalse.append(Assignment(ifFalse, o, virtual_only=True, parentStm=if_)) if_._outputs.append(o) o.drivers.append(if_) return o else: assert isinstance(val._dtype, Bits), val._dtype return val
def _get(self, numberOfBits: int, doCollect: bool): """ :param numberOfBits: number of bits to get from actual possition :param doCollect: if False output is not collected just iterator moves in structure """ if not isinstance(numberOfBits, int): numberOfBits = int(numberOfBits) while self.actuallyHave < numberOfBits: # accumulate while not has enought try: f = next(self.it) except StopIteration: if self.fillup and self.actual is not None: break else: raise NotEnoughtBitsErr() thisFieldLen = f._dtype.bit_length() if self.actual is None: if not doCollect and thisFieldLen <= numberOfBits: numberOfBits -= thisFieldLen else: self.actual = f self.actuallyHave = thisFieldLen else: if not doCollect and self.actuallyHave < numberOfBits: self.actuallyHave = thisFieldLen self.actual = f else: self.actuallyHave += thisFieldLen self.actual = f._concat(self.actual) # slice out from actual actual = self.actual actualOffset = self.actualOffset if self.actuallyHave < numberOfBits: assert self.fillup if doCollect: t = self.actual._dtype fillupW = numberOfBits - self.actuallyHave padding_t = Bits(fillupW, signed=t.signed, negated=t.negated) padding = padding_t.from_py(None) actual = padding._concat(actual) self.actuallyHave = 0 # update about what was taken self.actuallyHave -= numberOfBits self.actualOffset += numberOfBits if self.actuallyHave == 0: self.actual = None self.actualOffset = 0 if doCollect: if numberOfBits == 1: return actual[actualOffset] else: return actual[(actualOffset + numberOfBits):actualOffset]
def test_read(self, n=4 * (512 // 32), n2=2, magic=99, randomize=False): u = self.u in_addr_step = u.DATA_WIDTH // 8 out_addr_step = u.OUT_DATA_WIDTH // 8 in_words_in_out_word = out_addr_step // in_addr_step expected = [] for _ in range(n2): for in_i in range(n): u.s.ar._ag.data.append((in_i * in_addr_step, PROT_DEFAULT)) in_w = magic + in_i expected.append(in_w) m = Axi4LiteSimRam(u.m) in_t = Bits(u.DATA_WIDTH)[in_words_in_out_word] out_t = Bits(u.OUT_DATA_WIDTH) for out_w_i, out_w in enumerate( grouper(in_words_in_out_word, expected, padvalue=0)): w = in_t.from_py(out_w)._reinterpret_cast(out_t) m.data[out_w_i] = w t = n2 * n if randomize: self.randomize_all() t *= 5 self.runSim((t + 10) * CLK_PERIOD) self.assertValSequenceEqual(u.s.r._ag.data, [(v, 0) for v in expected])
def test_write(self, n=4 * (512 // 32), n2=2, magic=99, randomize=False): u = self.u in_addr_step = u.DATA_WIDTH // 8 out_addr_step = u.OUT_DATA_WIDTH // 8 in_words_in_out_word = out_addr_step // in_addr_step w_data = [] for _ in range(n2): for in_i in range(n): u.s.aw._ag.data.append((in_i * in_addr_step, PROT_DEFAULT)) in_w = magic + in_i w_data.append(in_w) u.s.w._ag.data.extend([(d, mask(in_addr_step)) for d in w_data]) m = Axi4LiteSimRam(u.m) in_t = Bits(u.DATA_WIDTH)[n] out_t = Bits(u.OUT_DATA_WIDTH)[n // in_words_in_out_word] t = n2 * n if randomize: self.randomize_all() t *= 5 self.runSim((t + 10) * CLK_PERIOD) v = m.getArray(0x0, out_addr_step, n // in_words_in_out_word) v = out_t.from_py(v)._reinterpret_cast(in_t) self.assertValSequenceEqual(v, w_data[n * (n2 - 1):])
def __setitem__(self, index, value): """ this []= operator can not be called in desing description, it can be only used to update HValues """ if not isinstance(self, HValue): raise TypeError( "To assign a member of hdl arrray/vector/list/... use a[index](val) instead of a[index] = val" ) # convert index to hSlice or hInt if isinstance(index, HValue): index = index elif isinstance(index, slice): length = self._dtype.bit_length() index = slice_to_SLICE(index, length) if not index._is_full_valid(): raise ValueError("invalid index", index) else: index = hInt(index) # convert value to bits of length specified by index if index._dtype == SLICE: Bits = self._dtype.__class__ itemT = Bits(index._size()) else: itemT = BIT if isinstance(value, HValue): value = value._auto_cast(itemT) else: value = itemT.from_py(value) return Bits3val.__setitem__(self, index, value)
def isCrossingWordBoundary(self, addr, rem): offset_t = Bits(self.getSizeAlignBits() + 1, signed=False) word_B = offset_t.from_py(self.DATA_WIDTH // 8) bytesInLastWord = rem._eq(0)._ternary(word_B, fitTo_t(rem, offset_t)) bytesAvaliableInLastWord = ( word_B - fitTo_t(addr[self.getSizeAlignBits():], offset_t)) crossesWordBoundary = rename_signal( self, bytesInLastWord > bytesAvaliableInLastWord, "crossesWordBoundary") return crossesWordBoundary
def __setitem__(self, index, value): """ this can not be called in desing description on non static values, only simulator can resolve this (in design use self[index] ** value instead of self[index] = value) """ # convert index to hSlice or hInt indexConst = True if not isinstance(index, Value): if isinstance(index, RtlSignalBase): if index._const: index = index.staticEval() else: indexConst = False elif isinstance(index, slice): length = self._dtype.bit_length() index = slice_to_SLICE(index, length) else: index = hInt(index) if indexConst and not index._is_full_valid(): indexConst = False # convert value to bits of length specified by index if indexConst: if index._dtype == SLICE: Bits = self._dtype.__class__ itemT = Bits(index._size()) else: itemT = BIT if not isinstance(value, Value): if isinstance(value, RtlSignalBase): if value._const: value = value.staticEval()._auto_cast(itemT) valueConst = True else: valueConst = False else: value = itemT.from_py(value) valueConst = True else: valueConst = True value = value._auto_cast(itemT) if indexConst and valueConst and isinstance(self, Value): return Bits3val.__setitem__(self, index, value) raise TypeError( "Only simulator can resolve []= for signals or invalid index")
def get_cachelines(self): u = self.u res = {} tags_t = u.tag_array.tag_record_t[u.WAY_CNT] tags_raw_t = Bits(tags_t.bit_length()) for index in range(2 ** u.INDEX_W): tags = self._get_from_mems(self.TAGS, index) tags = tags_raw_t.from_py(tags.val, tags.vld_mask)._reinterpret_cast(tags_t) for way, t in enumerate(tags): if t.valid: data = self.get_data(index, way) addr = u.deparse_addr(t.tag, index, 0) if data._is_full_valid(): data = int(data) res[int(addr)] = data return res
class AvalonMmSimRam(SimRam): """ Simulation memory for AvalonMM interfaces (slave component) """ def __init__(self, avalon_mm: AvalonMM, parent=None, clk=None, allow_unaligned_addr=False): """ :param clk: clk which should this memory use in simulation (if None the clk associated with an interface is used) :param avalon_mm: avalon_mm (AvalonMM master) interface to listen on :param parent: parent instance of this memory, memory will operate with same memory as parent one :attention: memories are commiting into memory in "data" property after transaction is complete """ DW = avalon_mm.DATA_WIDTH self.allow_unaligned_addr = allow_unaligned_addr SimRam.__init__(self, DW // 8, parent=parent) self.allMask = mask(self.cellSize) self.word_t = Bits(self.cellSize * 8) if clk is None: clk = avalon_mm._getAssociatedClk() self.bus = avalon_mm self.clk = clk self._registerOnClock() self.wPending = deque() def _registerOnClock(self): self.clk._sigInside.wait(self.checkRequests()) def checkRequests(self): """ Check if any request has appeared on interfaces """ yield WaitWriteOnly() if self.bus._ag.addrAg.data: rw, addr, burstCount = self.parseReq( self.bus._ag.addrAg.data.popleft()) if rw == READ_WRITE: self.doRead(addr, burstCount) self.wPending.append((addr, burstCount)) elif rw == READ: self.doRead(addr, burstCount) else: assert rw == WRITE, rw self.wPending.append((addr, burstCount)) wData = self.bus._ag.wData if wData and self.wPending[0][1] <= len(wData): addr, burstCount = self.wPending.popleft() for i in range(burstCount - 1): # consume the additional write requests which were generated during write data send _a, _ = self.wPending.popleft() assert int(addr) == int(_a), ( "inconsystent addres in write burst transaction", addr, burstCount, i, _a) self.doWrite(addr, burstCount) self._registerOnClock() def parseReq(self, req): rw, addr, burstCount = req try: addr = int(addr) except ValueError: raise AssertionError("Invalid AvalonMM request", req) from None try: burstCount = int(burstCount) except ValueError: raise AssertionError("Invalid AvalonMM request", req) from None return (rw, addr, burstCount) def doRead(self, addr, size): baseIndex = addr // self.cellSize if baseIndex * self.cellSize != addr: if not self.allow_unaligned_addr: raise ValueError("not aligned", addr) offset = addr % self.cellSize word_t = self.word_t word_mask0 = mask(8 * self.cellSize) word_mask1 = mask((self.cellSize - offset) * 8) else: offset = 0 mem = self.data for i in range(size): data = mem.get(baseIndex + i, None) if offset != 0: data1 = mem.get(baseIndex + i + 1, None) if data is None: if data1 is None: # data = None pass else: data = data1 << ((self.cellSize - offset) * 8) data = data & word_mask1 else: if data1 is None: if isinstance(data, int): data = word_t.from_py(data >> (offset * 8), word_mask0) else: data = ((data >> (offset * 8)) & word_mask0) \ | ((data1 << ((self.cellSize - offset) * 8)) & word_mask1) if data is None: raise AssertionError( "Invalid read of uninitialized value on addr 0x%x" % (addr + i * self.cellSize)) self.add_r_ag_data(data) def add_r_ag_data(self, data): self.bus._ag.rDataAg.data.append((data, RESP_OKAY)) def pop_w_ag_data(self): data, strb = self.bus._ag.wData.popleft() return (data, strb) def _write_single_word(self, data: HValue, strb: int, word_i: int): if strb == 0: return if strb != self.allMask: cur = self.data.get(word_i, None) if cur is None: cur_val = 0 cur_mask = 0 elif isinstance(cur, int): cur_val = cur cur_mask = self.allMask else: cur_val = cur.val cur_mask = cur.vld_mask for i in range(self.cellSize): if get_bit(strb, i): cur_val = set_bit_range(cur_val, i * 8, 8, get_bit_range(data.val, i * 8, 8)) cur_mask = set_bit_range( cur_mask, i * 8, 8, get_bit_range(data.vld_mask, i * 8, 8)) if cur_mask == self.allMask: data = cur_val else: data = self.word_t.from_py(cur_val, cur_mask) # print(f"data[{word_i:d}] = {data}") self.data[word_i] = data def doWrite(self, addr, size): baseIndex = addr // self.cellSize offset = addr % self.cellSize if offset and not self.allow_unaligned_addr: raise ValueError("not aligned", addr) for i in range(size): data, strb = self.pop_w_ag_data() strb = int(strb) if offset == 0: # print("alig", data, strb) self._write_single_word(data, strb, baseIndex + i) else: # print("init", data, strb) d0 = data << (offset * 8) strb0 = strb << offset d1 = (data >> (offset * 8)) & self.allMask strb1 = (strb >> offset) & mask(self.cellSize) # print("split", d0, d1, strb0, strb1) self._write_single_word(d0, strb0, baseIndex + i) self._write_single_word(d1, strb1, baseIndex + i + 1) self.doWriteAck() def doWriteAck(self): self.bus._ag.wRespAg.data.append(RESP_OKAY)
def connect_single_format_group(self, f_i: int, f: Union[str, AxiS_strFormatItem], strings_offset_and_size: Dict[Union[int, str], Tuple[int, int]], string_rom: RtlSignal, char_i: RtlSignal, to_bcd_inputs: List[Tuple[RtlSignal, HdlStatement]], en: RtlSignal): """ Connect a single formating group or string chunk to output. Depending on item type this may involve: * iterate the string characters stored in string_rom * iterate and translate bin/oct/hex characters * register bcd input for later connection * connect an input string from an input AxiStream """ dout = self.data_out in_vld = BIT.from_py(1) res = [] in_last = None string_rom_index_t = Bits(log2ceil(string_rom._dtype.size), signed=False) if isinstance(f, str): str_offset, str_size = strings_offset_and_size[f_i] str_offset = string_rom_index_t.from_py(str_offset) res.append( # read char of the string from string_rom dout.data(string_rom[str_offset + fitTo(char_i, str_offset, shrink=False)])) else: assert isinstance(f, AxiS_strFormatItem), f in_ = self.data_in._fieldsToInterfaces[f.member_path] if f.format_type in ('d', 'b', 'o', 'x', 'X'): if f.format_type == 'd': # first use BCD convertor to convert to BCD to_bcd_inputs.append((en, in_)) bcd = self.bin_to_bcd.dout in_vld = bcd.vld bcd.rd(dout.ready & en & char_i._eq(f.digits - 1)) in_ = bcd.data bits_per_char = AxiS_strFormatItem.BITS_PER_CHAR[f.format_type] actual_digit = self._sig(f"f_{f_i}_actual_digit", Bits(bits_per_char)) to_str_table_offset, _ = strings_offset_and_size[f.format_type] to_str_table_offset = string_rom_index_t.from_py( to_str_table_offset) # iterate output digits using char_i self.create_char_mux(in_, actual_digit, char_i, f.digits, bits_per_char) res.append( # use char translation table from string_rom to translate digit in to a char dout.data(string_rom[ to_str_table_offset + fitTo(actual_digit, to_str_table_offset, shrink=False)] )) str_size = f.digits else: # connect a string from an input AxiStream assert f.format_type == 's', f.format_type assert in_.DATA_WIDTH == 8, in_.DATA_WIDTH assert in_.USE_STRB == False, in_.USE_STRB assert in_.USE_KEEP == False, in_.USE_KEEP in_vld = in_.valid in_.ready(dout.ready & en) res.append(dout.data(in_.data)) in_last = in_.last if in_last is None: # if signal to detect last character is not overriden use conter to resolve it in_last = char_i._eq(str_size - 1) return res, in_vld, in_last,
class AxiSimRam(AxiDpSimRam): """ Simulation memory for Axi3/4 interfaces (slave component) """ def __init__(self, axi=None, axiAR=None, axiR=None, axiAW=None, axiW=None, axiB=None, parent=None, allow_unaligned_addr=False): """ :param clk: clk which should this memory use in simulation :param axi: axi (Axi3/4 master) interface to listen on :param axiAR, axiR, axiAW, axiW, axiB: splited interface use this if you do not have full axi interface :attention: use axi or axi parts not bouth :param parent: parent instance of this memory, memory will operate with same memory as parent one :attention: memories are commiting into memory in "data" property after transaction is complete """ if axi is not None: assert axiAR is None assert axiR is None assert axiAW is None assert axiW is None assert axiB is None if axi.HAS_R: axiAR = axi.ar axiR = axi.r if axi.HAS_W: axiAW = axi.aw axiW = axi.w axiB = axi.b if axiAR is not None: self.arAg = axiAR._ag self.rAg = axiR._ag DW = int(axiR.DATA_WIDTH) clk = axiAR._getAssociatedClk() else: assert axiR is None self.arAg = None self.rAg = None if axiAW is not None: self.awAg = axiAW._ag self.wAg = axiW._ag self.wAckAg = axiB._ag DW = int(axiW.DATA_WIDTH) clk = axiAW._getAssociatedClk() else: assert axiW is None assert axiB is None self.awAg = None self.wAg = None self.wAckAg = None assert axiAR is not None or axiAW is not None if self.wAg is not None: self.HAS_W_ID = hasattr(self.wAg.intf, "id") else: self.HAS_W_ID = False self.allow_unaligned_addr = allow_unaligned_addr SimRam.__init__(self, DW // 8, parent=parent) self.allMask = mask(self.cellSize) self.word_t = Bits(self.cellSize * 8) self.rPending = deque() self.wPending = deque() self.clk = clk self._registerOnClock() def parseReq(self, req): try: req = [int(v) for v in req] except ValueError: raise AssertionError("Invalid AXI request", req) from None _id = req[0] addr = req[1] size = req[4] + 1 return (_id, addr, size, self.allMask) def add_r_ag_data(self, _id, data, isLast): self.rAg.data.append((_id, data, RESP_OKAY, isLast)) def doRead(self): _id, addr, size, _ = self.rPending.popleft() baseIndex = addr // self.cellSize if baseIndex * self.cellSize != addr: if not self.allow_unaligned_addr: raise ValueError("not aligned", addr) offset = addr % self.cellSize word_t = self.word_t word_mask0 = mask(8 * self.cellSize) word_mask1 = mask((self.cellSize - offset) * 8) else: offset = 0 mem = self.data for i in range(size): isLast = i == size - 1 data = mem.get(baseIndex + i, None) if offset != 0: data1 = mem.get(baseIndex + i + 1, None) if data is None: if data1 is None: # data = None pass else: data = data1 << ((self.cellSize - offset) * 8) data = data & word_mask1 else: if data1 is None: if isinstance(data, int): data = word_t.from_py( data >> (offset * 8), word_mask0) else: data = ((data >> (offset * 8)) & word_mask0) \ | ((data1 << ((self.cellSize - offset) * 8)) & word_mask1) if data is None: raise AssertionError( "Invalid read of uninitialized value on addr 0x%x" % (addr + i * self.cellSize)) self.add_r_ag_data(_id, data, isLast) def pop_w_ag_data(self, _id): if self.HAS_W_ID: _id2, data, strb, last = self.wAg.data.popleft() _id2 = int(_id2) assert _id == _id2 else: data, strb, last = self.wAg.data.popleft() return (data, strb, last) def _write_single_word(self, data: HValue, strb: int, word_i: int): if strb == 0: return if strb != self.allMask: cur = self.data.get(word_i, None) if cur is None: cur_val = 0 cur_mask = 0 elif isinstance(cur, int): cur_val = cur cur_mask = self.allMask else: cur_val = cur.val cur_mask = cur.vld_mask for i in range(self.cellSize): if get_bit(strb, i): cur_val = set_bit_range( cur_val, i * 8, 8, get_bit_range(data.val, i * 8, 8)) cur_mask = set_bit_range( cur_mask, i * 8, 8, get_bit_range(data.vld_mask, i * 8, 8)) if cur_mask == self.allMask: data = cur_val else: data = self.word_t.from_py(cur_val, cur_mask) # print(f"data[{word_i:d}] = {data}") self.data[word_i] = data def doWrite(self): _id, addr, size, _ = self.wPending.popleft() baseIndex = addr // self.cellSize offset = addr % self.cellSize if offset and not self.allow_unaligned_addr: raise ValueError("not aligned", addr) for i in range(size): data, strb, last = self.pop_w_ag_data(_id) strb = int(strb) last = int(last) last = bool(last) isLast = i == size - 1 assert last == isLast, (addr, size, i) if offset == 0: # print("alig", data, strb) self._write_single_word(data, strb, baseIndex + i) else: # print("init", data, strb) d0 = data << (offset * 8) strb0 = strb << offset d1 = (data >> (offset * 8)) & self.allMask strb1 = (strb >> offset) & mask(self.cellSize) # print("split", d0, d1, strb0, strb1) self._write_single_word(d0, strb0, baseIndex + i) self._write_single_word(d1, strb1, baseIndex + i + 1) self.doWriteAck(_id) def doWriteAck(self, _id): self.wAckAg.data.append((_id, RESP_OKAY))