コード例 #1
0
    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
コード例 #2
0
ファイル: hvalue_test.py プロジェクト: mfkiwl/hwtLib
    def test_BitsFromIncompatibleType(self):
        t = Bits(2)
        with self.assertRaises(ValueError):
            t.from_py("a1")

        with self.assertRaises(TypeError):
            t.from_py(object())
コード例 #3
0
 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)
コード例 #4
0
ファイル: math.py プロジェクト: pradeepchawda/hwt
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
コード例 #5
0
ファイル: hvalue_test.py プロジェクト: mfkiwl/hwtLib
    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)
コード例 #6
0
ファイル: operators_test.py プロジェクト: mfkiwl/hwtLib
    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)
コード例 #7
0
ファイル: ops.py プロジェクト: klopstock/hwt
 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
コード例 #8
0
ファイル: vectorUtils.py プロジェクト: kermit0124/hwt
    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]
コード例 #9
0
ファイル: resize_test.py プロジェクト: mfkiwl/hwtLib
    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])
コード例 #10
0
ファイル: resize_test.py プロジェクト: mfkiwl/hwtLib
    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):])
コード例 #11
0
ファイル: bitsVal.py プロジェクト: saislam/hwt
    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)
コード例 #12
0
ファイル: base.py プロジェクト: mfkiwl/hwtLib
 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
コード例 #13
0
ファイル: bitsVal.py プロジェクト: kermit0124/hwt
    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")
コード例 #14
0
 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
コード例 #15
0
ファイル: ram.py プロジェクト: mfkiwl/hwtLib
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)
コード例 #16
0
    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,
コード例 #17
0
ファイル: ram.py プロジェクト: mfkiwl/hwtLib
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))