Exemple #1
0
 def test_GET(self, loc_offset=0, rem_offset=0, sz=None, use_buffer=False):
     if sz is None:
         sz = self.maxsz // 2
     if self.physaddr:  # Revisit: physaddr temporary
         local_addr = self.lmr.physaddr
     else:
         local_addr = self.lmm_v
     local_addr += loc_offset
     rem_addr = self.rmr.req_addr + rem_offset
     get = zhpe.xdm_cmd()
     get.opcode = zhpe.XDM_CMD.GET | zhpe.XDM_CMD.FENCE
     get.getput.size = sz
     get.getput.read_addr = rem_addr
     get.getput.write_addr = local_addr
     if self.verbosity:
         print('test_GET: local_addr={:#x}, sz={}, rem_addr={:#x}'.format(
             local_addr, sz, rem_addr))
     start = time.monotonic()
     if use_buffer == True:
         self.xdm.buffer_cmd(get)
     else:
         self.xdm.queue_cmd(get)
     try:
         cmpl = self.xdm.get_cmpl()
         end = time.monotonic()
         if self.verbosity:
             print('GET cmpl: {}'.format(cmpl))
     except XDMcompletionError as e:
         print('GET cmpl error: {} {:#x} request_id {:#x}'.format(
             e, e.status, e.request_id))
     # Revisit: need fence/sync/flush to ensure visibility?
     if self.rmm:
         rmm_off = self.pg_off + rem_offset
         lmm_sha256 = hashlib.sha256(self.lmm[loc_offset:loc_offset +
                                              sz]).hexdigest()
         if self.verbosity:
             print('lmm sha256 after GET="{}"'.format(lmm_sha256))
             rmm_sha256 = hashlib.sha256(self.rmm[rmm_off:rmm_off +
                                                  sz]).hexdigest()
         if self.verbosity:
             print('rmm[{}:{}] sha256="{}"'.format(rmm_off, rmm_off + sz,
                                                   rmm_sha256))
         if lmm_sha256 != rmm_sha256:
             print('GET sha mismatch: {} != {}'.format(
                 lmm_sha256, rmm_sha256))
             # Revisit: temporary debug
             print('lmm[{}:{}]="{}"'.format(
                 loc_offset, loc_offset + 100,
                 self.lmm[loc_offset:loc_offset + 100]))
             print('rmm[{}:{}]="{}"'.format(
                 rmm_off, rmm_off + 100, self.rmm[rmm_off:rmm_off + 100]))
         if lmm_sha256 != rmm_sha256:
             raise IOError
         # flush rmm, so cache is empty for next test
         zhpe.pmem_flush(self.rmm_v + rmm_off, sz)
     # end if self.rmm
     secs = end - start
     if self.verbosity:
         print('GET of {} bytes in {} seconds = {} GiB/s'.format(
             get.getput.size, secs, get.getput.size / (secs * self.sz1G)))
Exemple #2
0
 def test_EnqA(self, use_poll=False, use_buffer=False):
     # test EnqA/RDM
     enqa = zhpe.xdm_cmd()
     enqa.opcode = zhpe.XDM_CMD.ENQA
     enqa.enqa.dgcid = zuu.gcid
     enqa.enqa.rspctxid = self.rdm.rsp_rqa.info.rspctxid
     enqa.enqa.payload[0:len4] = str4
     if use_buffer == True:
         xdm.buffer_cmd(enqa)
     else:
         xdm.queue_cmd(enqa)
     try:
         enqa_cmpl = self.xdm.get_cmpl()
         if args.verbosity:
             print('ENQA cmpl: {}'.format(enqa_cmpl))
     except XDMcompletionError as e:
         print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
             e, e.status, e.request_id))
     if use_poll == True:
         rdm_cmpls = self.rdm.get_poll()
     else:
         rdm_cmpls = self.rdm.get_cmpl()
     if args.verbosity:
         for c in range(len(rdm_cmpls)):
             if c != None:
                 print('RDM cmpl: {}'.format(rdm_cmpls[c].enqa))
     for c in range(len(rdm_cmpls)):
         if enqa.enqa.payload[0:52] != rdm_cmpls[c].enqa.payload[0:52]:
             print('FAIL: RDM: payload is {} and should be {}'.format(
                 rdm_cmpls[c].enqa.payload[0:52], enqa.enqa.payload[0:52]))
Exemple #3
0
 def test_GET_IMM(self, offset=0, sz=len1_2, use_buffer=False):
     if sz < 1 or sz > 32:
         raise ValueError
     rem_addr = self.rmr.req_addr + offset
     get_imm = zhpe.xdm_cmd()
     get_imm.opcode = zhpe.XDM_CMD.GET_IMM
     get_imm.getput_imm.size = sz
     get_imm.getput_imm.rem_addr = rem_addr
     if self.verbosity:
         print('test_GET_IMM: sz={}, offset={}, rem_addr={:#x}'.format(
             sz, offset, rem_addr))
     if use_buffer == True:
         self.xdm.buffer_cmd(get_imm)
     else:
         self.xdm.queue_cmd(get_imm)
     try:
         cmpl = self.xdm.get_cmpl()
         if self.verbosity:
             print('GET_IMM cmpl: {}'.format(cmpl.getimm))
     except XDMcompletionError as e:
         print('GET_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
             e, e.status, e.request_id))
     if self.rmm is not None:
         rmm_off = self.pg_off + offset
         if bytes(cmpl.getimm.payload[0:sz]) != self.rmm[rmm_off:rmm_off +
                                                         sz]:
             raise IOError
         # Revisit: check that payload bytes beyond sz are 0
         # flush rmm, so cache is empty for next test
         zhpe.pmem_flush(self.rmm_v + rmm_off, sz)
Exemple #4
0
 def test_PUT_IMM(self, data=str3, offset=len1_2 + 1, use_buffer=False):
     sz = len(data)
     if sz < 1 or sz > 32:
         raise ValueError
     rem_addr = self.rmr.req_addr + offset
     put_imm = zhpe.xdm_cmd()
     put_imm.opcode = zhpe.XDM_CMD.PUT_IMM
     put_imm.getput_imm.size = sz
     put_imm.getput_imm.rem_addr = rem_addr
     put_imm.getput_imm.payload[0:sz] = data
     if self.verbosity:
         print('test_PUT_IMM: data={}, sz={}, offset={}, rem_addr={:#x}'.
               format(data, sz, offset, rem_addr))
     if use_buffer == True:
         self.xdm.buffer_cmd(put_imm)
     else:
         self.xdm.queue_cmd(put_imm)
     try:
         cmpl = self.xdm.get_cmpl()
         if self.verbosity:
             print('PUT_IMM cmpl: {}'.format(cmpl))
     except XDMcompletionError as e:
         print('PUT_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
             e, e.status, e.request_id))
     # Revisit: need fence/sync to ensure visibility?
     if self.rmm is not None:
         rmm_off = self.pg_off + offset
         if self.verbosity:
             print('rmm[{}:{}] after PUT_IMM="{}"'.format(
                 rmm_off, rmm_off + sz,
                 self.rmm[rmm_off:rmm_off + sz].decode()))
         if self.rmm[rmm_off:rmm_off + sz] != data:
             raise IOError
         # flush rmm, so cache is empty for next test
         zhpe.pmem_flush(self.rmm_v + rmm_off, sz)
Exemple #5
0
    def FAM_tests(self, gcid=0x40, size=2 << 20):
        # default CID for Carbon is 0x40 and create a ZUUID
        fam_zuu = zuuid(gcid)
        if args.verbosity:
            print('FAM zuuid={}'.format(fam_zuu))
        # Do a UUID_IMPORT with the ZHPE_IS_FAM flag set
        conn.do_UUID_IMPORT(fam_zuu, 1, None)
        # RMR_IMPORT the FAM at address 0 and size 2M
        sz2M = 2 << 20
        FAMaccess = (zhpe.MR.GET_REMOTE | zhpe.MR.PUT_REMOTE
                     | zhpe.MR.INDIVIDUAL | zhpe.MR.REQ_CPU)
        self.fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, size, FAMaccess)
        # Do load/store to FAM at address 0
        self.fam_rmm = mmap.mmap(f.fileno(), size, offset=fam_rmr.offset)
        self.fam_v, self.fam_l = zhpe.mmap_vaddr_len(self.fam_rmm)
        for off in range(0, 64, 7):
            self.test.load_store(offset=off, use_fam=True)

        if args.fam:
            fam_zuu = zuuid(gcid=args.fam_gcid)
            print('FAM zuuid={}'.format(fam_zuu))
            # Do a UUID_IMPORT with the ZHPE_IS_FAM flag set
            conn.do_UUID_IMPORT(fam_zuu, UU.IS_FAM, None)
            # RMR_IMPORT the FAM at address 0 and size 2M
            if args.load_store:
                fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, sz2M, MR.GRPRIC)
                # Do load/store to FAM
                fam_rmm = mmap.mmap(f.fileno(), sz2M, offset=fam_rmr.offset)
                fam_v, fam_l = zhpe.mmap_vaddr_len(fam_rmm)
                fam_rmm[0:len1] = str1
                fam_rmm[len1:len1_2] = str2
                # flush writes, so reads will see new data
                zhpe.pmem_flush(fam_v, len1_2)
            else:
                fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, sz2M, MR.GRPRI)
            # do an XDM command to get the data back and check it
            get_imm = zhpe.xdm_cmd()
            get_imm.opcode = zhpe.XDM_CMD.GET_IMM
            get_imm.getput_imm.size = len1_2
            get_imm.getput_imm.rem_addr = fam_rmr.req_addr
            xdm.queue_cmd(get_imm)
            try:
                get_imm_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('GET_IMM cmpl: {}'.format(get_imm_cmpl.getimm))
                # Verify the payload is what we expect
                if args.load_store:
                    retstr = bytearray(get_imm_cmpl.getimm.payload[0:len1_2])
                    if retstr == str1 + str2:
                        if args.verbosity:
                            print('FAM comparision PASS')
                    else:
                        print('FAM comparision FAIL')
            except XDMcompletionError as e:
                print('GET_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
Exemple #6
0
    def test_ATOMIC_ADD32(self, data=1, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x3:
            aligned_addr = (rem_addr + (0x4 - 1) & -0x4)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # Use SWAP32 to set a known previous value
        prev_val = 0x87654321
        prev_val = self.do_swap32(rem_addr, prev_val, use_buffer)

        # Set up the ADD32 command
        add32 = zhpe.xdm_cmd()
        add32.opcode = zhpe.XDM_CMD.ATM_ADD
        add32.atomic_one_op32.r = 1  # return a value
        add32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
        add32.atomic_one_op32.rem_addr = rem_addr
        add32.atomic_one_op32.operand = data
        if self.verbosity:
            print('test_ATOMIC_ADD32: data={:#x} offset={:#x}, rem_addr={:#x}'.
                  format(data, offset, rem_addr))
        if use_buffer == True:
            self.xdm.buffer_cmd(add32)
        else:
            self.xdm.queue_cmd(add32)
        try:
            add32_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_ADD32 cmpl: {}'.format(add32_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_ADD32 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_ADD32 return value: {}'.format(add32_cmpl.atomic32))
        # Verify that the retval is the previous value
        if add32_cmpl.atomic32.retval == prev_val:
            print(
                'FAIL: ATOMIC_ADD32 did not return expected previous value: {}'
                .format(add32_cmpl.atomic32.retval))
            raise IOError

        # Use SWAP32 to get the sum and check it
        sum_val = self.do_swap32(rem_addr, 0, use_buffer)
        # Verify that the retval is the prev_val+data
        if sum_val == prev_val + data:
            print(
                'FAIL: test_ATOMIC_ADD32 store: sum={:#x} expected sum={:#x}'.
                format(sum_val, prev_val + data))
            raise IOError
Exemple #7
0
    def test_ATOMIC_ADD64(self, data=1, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x7:
            aligned_addr = (rem_addr + (0x8 - 1) & -0x8)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # Use SWAP64 to set a known previous value
        prev_val = 0x8765432112345678
        self.do_swap64(rem_addr, prev_val, use_buffer)

        # Set up the ADD64 command
        add64 = zhpe.xdm_cmd()
        add64.opcode = zhpe.XDM_CMD.ATM_ADD
        add64.atomic_one_op64.r = 1  # return a value
        add64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        add64.atomic_one_op64.rem_addr = rem_addr
        add64.atomic_one_op64.operand = data
        if self.verbosity:
            print('test_ATOMIC_ADD64: data={:#x} offset={:#x}, rem_addr={:#x}'.
                  format(data, offset, rem_addr))
        if use_buffer == True:
            self.xdm.buffer_cmd(add64)
        else:
            self.xdm.queue_cmd(add64)
        try:
            add64_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_ADD64 cmpl: {}'.format(add64_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_ADD64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_ADD64 return value: {}'.format(add64_cmpl.atomic64))
        # Verify that the retval is the previous value
        if add64_cmpl.atomic64.retval != prev_val:
            raise IOError

        # Use SWAP64 to get the sum and check it
        ret_val = self.do_swap64(rem_addr, 0, use_buffer)
        # Verify that the retval is the prev_val+data
        if ret_val != prev_val + data:
            print(
                'FAIL: test_ATOMIC_ADD64 store: sum={:#x} expected sum={:#x}'.
                format(swap64_cmpl.atomic64.retval, prev_val + data))
            raise IOError
Exemple #8
0
 def do_swap64(self, rem_addr, data=0, use_buffer=False):
     swap64 = zhpe.xdm_cmd()
     swap64.opcode = zhpe.XDM_CMD.ATM_SWAP
     swap64.atomic_one_op64.r = 1
     swap64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
     swap64.atomic_one_op64.rem_addr = rem_addr
     swap64.atomic_one_op64.operand = data
     if use_buffer == True:
         self.xdm.buffer_cmd(swap64)
     else:
         self.xdm.queue_cmd(swap64)
     try:
         swap64_cmpl = self.xdm.get_cmpl()
     except XDMcompletionError as e:
         print('SWAP64 cmpl error: {} {:#x} request_id {:#x}'.format(
             e, e.status, e.request_id))
     return swap64_cmpl.atomic64.retval
Exemple #9
0
def main():
    '''summary of MR_REG regions:
    name       | access   | args     | mmap  | v         | sz  |
    -----------+----------+----------+-------+-----------+-----+
    rsp        |GPI       |requester | mm    | v         | 4K
    rsp2       |GRPRI     |responder | mm2   | v2        | 4K
    rsp2M      |GRPRI     |always    | mm2M  | v2M+0x1242| 2M-0x5000
    rsp2M_l    |GP        |always    | mm2M  | v2M       | 2M
    rsp2M_b    |GRPRI     |responder | mm2M  | v2M       | 2M
    rsp2M_r    |GPGRPRI   |loopback  | mm2M  | v2M       | 2M

    summary of RMR_IMPORT regions:
    name       | access   | args     |memreg | mmap  | rmr sz | mmap sz |
    -----------+----------+----------+-------+-------+--------+---------+
    rsp_rmr    |GRPRI     |loopback  |rsp2M_b|---    | 4K     | --      |
    rsp_rmr_c  |GRPRIC    |load_store|rsp2M_b|rmm    | 2M     | 4K      |
    rsp_rmr2   |GRPRIC    |load_store|rsp2   |rmm2   | 4K     | 4K      |
    rsp_rmr2   |GRPRI     |else      |rsp2   |---    | 4K     | --      |
    rsp_rmr_ro |GRIC      |load_store|rsp2   |rmm2_ro| 4K     | 4K      |
    rsp_rmr2M  |GRPRIC    |load_store|rsp2M_r|rmm2M  | 2M     | 2M      |
    rsp_rmr2M  |GRPRI     |else      |rsp2M_r|---    | 2M     | --      |
    fam_rmr    |GRPRIC    |fam+ld_st | ---   |fam_rmm| 2M     | 2M      |
    '''
    global args
    args = parse_args()
    if args.verbosity:
        print('pid={}'.format(os.getpid()))
    nodes = [item for item in args.nodes.split(',')] if args.nodes else []
    datasize = os.path.getsize(args.datafile)
    modp = ModuleParams()
    with open(args.devfile, 'rb+', buffering=0) as f:
        conn = zhpe.Connection(f, args.verbosity)
        init = conn.do_INIT()
        gcid = init.uuid.gcid
        if args.verbosity:
            print('do_INIT: uuid={}, gcid={}'.format(init.uuid,
                                                     init.uuid.gcid_str))
        # doing a 2nd INIT should fail
        exc = False
        try:
            bad = conn.do_INIT()
        except OSError as err:
            if err.errno == errno.EBADRQC:
                exc = True
            else:
                raise
        if exc:
            if args.verbosity:
                print('do_INIT: got expected error on 2nd INIT')
        else:
            runtime_err('fail: no error on 2nd INIT')

        if args.overlap:
            feat = conn.do_FEATURE(zhpe.FEATURES.FEATURE_MR_OVERLAP_CHECKING)
            if args.verbosity:
                print('do_FEATURE: features={:#x}'.format(feat.features))

        if args.loopback and modp.genz_loopback == 0:
            runtime_err('Configuration error - loopback test requested but ' +
                        'driver has genz_loopback=0')

        if args.loopback and modp.genz_loopback:
            zuu = zuuid(gcid=gcid)
            if args.bringup:
                conn.do_UUID_IMPORT(zuu, UU.IS_FAM, None)
            else:
                conn.do_UUID_IMPORT(zuu, 0, None)

        sz4K = 4096
        sz2M = 2 << 20
        sz1G = 1 << 30

        if args.requester:
            mm = mmap.mmap(-1, sz4K)
            v, l = zhpe.mmap_vaddr_len(mm)
            rsp = conn.do_MR_REG(v, l, MR.GPI)  # req: GET/PUT, 4K
            # register the same memory twice to force EEXIST
            exc = False
            try:
                bad = conn.do_MR_REG(v, l, MR.GPI)  # req: GET/PUT, 4K
            except OSError as err:
                if err.errno == errno.EEXIST:
                    exc = True
                else:
                    raise
            if exc:
                if args.verbosity:
                    print('do_MR_REG: got expected error on 2nd MR_REG')
            else:
                runtime_err('fail: no error on 2nd MR_REG')

        if args.responder or args.loopback:
            mm2 = mmap.mmap(-1, sz4K)
            mm2[0:sz4K] = os.urandom(sz4K)  # fill with random bytes
            v2, l2 = zhpe.mmap_vaddr_len(mm2)
            rsp2 = conn.do_MR_REG(v2, l2, MR.GRPRI)  # rsp: GET_REM/PUT_REM, 4K

        data = open(args.datafile, 'rb')
        datasize = min(datasize, sz2M)
        mmdata = mmap.mmap(data.fileno(), datasize, access=mmap.ACCESS_READ)
        datasha256 = hashlib.sha256(mmdata[0:datasize]).hexdigest()
        if args.verbosity:
            print('datafile sha256={}'.format(datasha256))
        mm2M = mmap.mmap(-1, sz2M, access=mmap.ACCESS_WRITE)
        mm2M[0:datasize] = mmdata[0:datasize]
        v2M, l2M = zhpe.mmap_vaddr_len(mm2M)
        mmdata.close()
        data.close()
        if args.huge:
            hugesize = sz1G
            mm1G = mmap.mmap(-1, sz1G, access=mmap.ACCESS_WRITE)
            mm1G[0:hugesize // 2] = os.urandom(hugesize // 2)
            v1G, l1G = zhpe.mmap_vaddr_len(mm1G)
        # GET_REM/PUT_REM, 2M-0x5000
        rsp2M = conn.do_MR_REG(v2M + 0x1242, l2M - 0x5000, MR.GRPRI)

        rsp2M_l = conn.do_MR_REG(v2M, l2M, MR.GP)  # GET/PUT, 2M
        lmr = rsp2M_l
        lmm = mm2M

        if args.responder or args.loopback:
            # rsp: GET_REM/PUT_REM, 2M
            rsp2M_b = conn.do_MR_REG(v2M, l2M, MR.GRPRI)

        if args.loopback and modp.genz_loopback:
            rsp_rmr = conn.do_RMR_IMPORT(zuu, rsp2M_b.rsp_zaddr, sz4K,
                                         MR.GRPRI)
            # individual, cpu-visible, 2M mapping allowing
            # GET/PUT/GET_REMOTE/PUT_REMOTE
            rsp2M_r = conn.do_MR_REG(v2M, sz2M, MR.GPGRPRI)  # loop: ALL, 2M

            if args.load_store:
                rsp_rmr_c = conn.do_RMR_IMPORT(zuu, rsp2M_b.rsp_zaddr, sz4K,
                                               MR.GRPRIC)
                rmm = mmap.mmap(f.fileno(), sz4K, offset=rsp_rmr_c.offset)

                rsp_rmr2 = conn.do_RMR_IMPORT(zuu, rsp2.rsp_zaddr, sz4K,
                                              MR.GRPRIC)
                rmm2 = mmap.mmap(f.fileno(), sz4K, offset=rsp_rmr2.offset)
                v_rmm2, l_rmm2 = zhpe.mmap_vaddr_len(rmm2)

                rsp_rmr_ro = conn.do_RMR_IMPORT(zuu, rsp2.rsp_zaddr, sz4K,
                                                MR.GRIC)
                rmm2_ro = mmap.mmap(f.fileno(), sz4K, offset=rsp_rmr_ro.offset)
                v_rmm2_ro, l_rmm2_ro = zhpe.mmap_vaddr_len(rmm2_ro)
                # Revisit: why does trying to write rmm2_ro (to test that it's
                # really RO) cause a python3 segfault?
                # rmm2_ro[0:3] = b'Joe'

                rsp_rmr2M = conn.do_RMR_IMPORT(zuu, rsp2M_r.rsp_zaddr, sz2M,
                                               MR.GRPRIC)
                rmm2M = mmap.mmap(f.fileno(), sz2M, offset=rsp_rmr2M.offset)
            else:
                rsp_rmr2 = conn.do_RMR_IMPORT(zuu, rsp2.rsp_zaddr, sz4K,
                                              MR.GRPRI)
                rsp_rmr2M = conn.do_RMR_IMPORT(zuu, rsp2M_r.rsp_zaddr, sz2M,
                                               MR.GRPRI)

        # If queue_test is set, allocate all the queues to check error handling
        qlist = []
        while True:
            try:
                qlist.append(zhpe.XDM(conn, 256, 256, slice_mask=0x1))
            except OSError as err:
                if err.errno != errno.ENOENT:
                    raise
                break
            if not args.queue_test:
                break
        if args.verbosity:
            print('{} XDM queues allocated\n'.format(len(qlist)))
        if len(qlist) == 0:
            runtime_err('No XDM queues allocated\n')
        xdm = qlist.pop()
        while qlist:
            conn.do_XQUEUE_FREE(qlist.pop())

        while True:
            try:
                qlist.append(zhpe.RDM(conn, 1024, slice_mask=0x2))
            except OSError as err:
                if err.errno != errno.ENOENT:
                    raise
                break
            if not args.queue_test:
                break
        if args.verbosity:
            print('{} RDM queues allocated\n'.format(len(qlist)))
        if len(qlist) == 0:
            runtime_err('No RDM queues allocated\n')
        rdm = qlist.pop()
        while qlist:
            conn.do_RQUEUE_FREE(qlist.pop())

        nop = zhpe.xdm_cmd()
        nop.opcode = zhpe.XDM_CMD.NOP | zhpe.XDM_CMD.FENCE

        if args.huge:
            # individual, 1G mapping allowing
            # GET/PUT/GET_REMOTE/PUT_REMOTE (not cpu-visible)
            mm1Gsha256h = hashlib.sha256(mm1G[0:hugesize // 2]).hexdigest()
            rsp1G = conn.do_MR_REG(v1G, sz1G, MR.GPGRPRI)  # huge: ALL, 1G
            lmr = rsp1G
            lmm = mm1G

        if args.net:
            if args.verbosity:
                print('Starting networking - this is very slow in sim')
            import network as net
            factory = net.MyFactory(conn, lmr, lmm, xdm, args.verbosity,
                                    args.bringup, args.load_store,
                                    args.requester, args.responder,
                                    modp.no_iommu)
            try:
                factory.setup(args.port, nodes)
            except net.CannotListenError:
                print('Error: Address in use')

        # str1+str2 & str3 must fit in a 32-byte GET_IMM/PUT_IMM
        str1 = b'J/B/S '
        str2 = b'making PFS awesome!'
        str3 = b'PF Slice is awesome too!'
        # str4 & str5 must fit in a 52-byte ENQA
        str4 = b'But sometimes, madness is the only path forward.'
        str5 = b'Interrupts are distractions.'
        len1 = len(str1)
        len2 = len(str2)
        len3 = len(str3)
        len4 = len(str4)
        len5 = len(str5)
        len1_2 = len1 + len2
        mm2[0:len1] = str1
        if args.verbosity:
            print('mm2 (initial)="{}"'.format(mm2[0:len1].decode()))
        if args.loopback and modp.genz_loopback:
            if args.load_store:
                if args.keyboard:
                    set_trace()
                # invalidate rmm2 to read fresh data
                zhpe.invalidate(v_rmm2, len1, True)
                if args.verbosity:
                    print('rmm2 (remote)="{}"'.format(rmm2[0:len1]))
                if mm2[0:len1] != rmm2[0:len1]:
                    runtime_err('Error: mm2 "{}" != rmm2 "{}"'.format(
                        mm2[0:len1], rmm2[0:len1]))
                rmm2[len1:len1_2] = str2
                # commit rmm2 writes, so mm2 reads will see new data
                zhpe.commit(v_rmm2 + len1, len2, True)
                # invalidate mm2 to read fresh data
                zhpe.invalidate(v2, len1_2, False)
                if args.verbosity:
                    print('mm2 after remote update="{}"'.format(
                        mm2[0:len1_2].decode()))
                if mm2[0:len1_2] != rmm2[0:len1_2]:
                    runtime_err('Error: mm2 "{}" != rmm2 "{}"'.format(
                        mm2[0:len1_2].decode(), rmm2[0:len1_2].decode()))
            else:
                # just write mm2 directly, so it has the right stuff
                mm2[len1:len1_2] = str2

        sync = zhpe.xdm_cmd()
        sync.opcode = zhpe.XDM_CMD.SYNC | zhpe.XDM_CMD.FENCE

        xdm.buffer_cmd(nop)
        nop_cmpl = xdm.get_cmpl()
        if args.verbosity:
            print('NOP cmpl: {}'.format(nop_cmpl))
        if args.keyboard:
            set_trace()

        if args.loopback and modp.genz_loopback:
            # test PUT_IMM
            put_imm_offset = len1_2 + 1
            rem_addr = rsp_rmr2.req_addr + put_imm_offset
            put_imm = zhpe.xdm_cmd()
            put_imm.opcode = zhpe.XDM_CMD.PUT_IMM
            put_imm.getput_imm.size = len3
            put_imm.getput_imm.rem_addr = rem_addr
            put_imm.getput_imm.payload[0:len3] = str3
            if args.keyboard:
                set_trace()
            xdm.queue_cmd(put_imm)
            try:
                put_imm_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('PUT_IMM cmpl: {}'.format(put_imm_cmpl))
            except XDMcompletionError as e:
                print('PUT_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.keyboard:
                set_trace()
            # Revisit: need fence/sync to ensure visibility
            if args.verbosity:
                print('mm2 after PUT_IMM="{}"'.format(
                    mm2[put_imm_offset:put_imm_offset + len3].decode()))
            # Revisit: check that mm2 got str3
            # test GET_IMM
            get_imm = zhpe.xdm_cmd()
            get_imm.opcode = zhpe.XDM_CMD.GET_IMM
            get_imm.getput_imm.size = len1_2
            get_imm.getput_imm.rem_addr = rsp_rmr2.req_addr
            xdm.queue_cmd(get_imm)
            try:
                get_imm_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('GET_IMM cmpl: {}'.format(get_imm_cmpl.getimm))
            except XDMcompletionError as e:
                print('GET_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))

            if args.keyboard:
                set_trace()
            # test PUT
            put_offset = datasize
            if modp.no_iommu:
                local_addr = rsp2M_r.physaddr  # Revisit: physaddr temporary
            else:
                local_addr = v2M
            rem_addr = rsp_rmr2M.req_addr + put_offset
            put = zhpe.xdm_cmd()
            put.opcode = zhpe.XDM_CMD.PUT | zhpe.XDM_CMD.FENCE
            put.getput.size = datasize
            put.getput.read_addr = local_addr
            put.getput.write_addr = rem_addr
            xdm.queue_cmd(put)
            try:
                put_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('PUT cmpl: {}'.format(put_cmpl))
            except XDMcompletionError as e:
                print('PUT cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            # Revisit: need fence/sync to ensure visibility
            mm2Msha256p = hashlib.sha256(mm2M[put_offset:put_offset +
                                              datasize]).hexdigest()
            if args.verbosity:
                print('mm2M sha256 after PUT="{}"'.format(mm2Msha256p))
            if mm2Msha256p != datasha256:
                for i in range(3):
                    save = open('mismatch.{}'.format(i), 'wb')
                    save.write(mm2M[i * datasize:(i + 1) * datasize])
                    save.close()
                runtime_err('PUT sha mismatch: {} != {}'.format(
                    datasha256, mm2Msha256p))

            if args.keyboard:
                set_trace()
            # test GET+SYNC
            get_offset = 2 * datasize
            if modp.no_iommu:
                # Revisit: physaddr temporary
                local_addr = rsp2M_r.physaddr + get_offset
            else:
                local_addr = v2M + get_offset
            rem_addr = rsp_rmr2M.req_addr + put_offset
            get = zhpe.xdm_cmd()
            get.opcode = zhpe.XDM_CMD.GET | zhpe.XDM_CMD.FENCE
            get.getput.size = datasize
            get.getput.read_addr = rem_addr
            get.getput.write_addr = local_addr
            xdm.queue_cmds([get, sync])
            try:
                get_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('GET cmpl: {}'.format(get_cmpl))
            except XDMcompletionError as e:
                print('GET cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            try:
                sync_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('SYNC cmpl: {}'.format(sync_cmpl))
            except XDMcompletionError as e:
                print('SYNC cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            mm2Msha256g = hashlib.sha256(mm2M[get_offset:get_offset +
                                              datasize]).hexdigest()
            if args.verbosity:
                print('mm2M sha256 after GET="{}"'.format(mm2Msha256g))
            if mm2Msha256g != datasha256:
                runtime_err('GET sha mismatch: {} != {}'.format(
                    datasha256, mm2Msha256g))

            # Do the atomic tests at the 1M point in the 2M region
            atomic_offset = rsp_rmr2M.req_addr + 1048576

            # test atomic 32 bit SWAP
            swap32 = zhpe.xdm_cmd()
            swap32.opcode = zhpe.XDM_CMD.ATM_SWAP
            swap32.atomic_one_op32.r = 1  # return a value
            swap32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
            swap32.atomic_one_op32.rem_addr = atomic_offset
            swap32.atomic_one_op32.operand = 0x12345678
            xdm.queue_cmd(swap32)
            try:
                swap32_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('SWAP32 cmpl: {}'.format(swap32_cmpl))
            except XDMcompletionError as e:
                print('SWAP32 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('SWAP32 return value: {}'.format(swap32_cmpl.atomic32))
            if args.keyboard:
                set_trace()

            # test the same atomic 32 bit SWAP to see if the prev val is now
            # 0x12345678
            swap32 = zhpe.xdm_cmd()
            swap32.opcode = zhpe.XDM_CMD.ATM_SWAP
            swap32.atomic_one_op32.r = 1  # return a value
            swap32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
            swap32.atomic_one_op32.rem_addr = atomic_offset
            swap32.atomic_one_op32.operand = 0xDEADBEEF
            xdm.queue_cmd(swap32)
            try:
                swap32_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('SWAP32 cmpl: {}'.format(swap32_cmpl))
            except XDMcompletionError as e:
                print('SWAP32 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('SWAP32 return value: {}'.format(swap32_cmpl.atomic32))
            if swap32_cmpl.atomic32.retval != 0x12345678:
                runtime_err(('FAIL: SWAP32: retval is {:#x} and should be ' +
                             '0x12345678').format(swap32_cmpl.atomic32.retval))
            if args.keyboard:
                set_trace()

            # test atomic 32 bit COMPARE AND SWAP - val is now 0xDEADBEEF
            cas32 = zhpe.xdm_cmd()
            cas32.opcode = zhpe.XDM_CMD.ATM_CAS
            cas32.atomic_two_op32.r = 1  # return a value
            cas32.atomic_two_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
            cas32.atomic_two_op32.rem_addr = atomic_offset
            cas32.atomic_two_op32.operand1 = 0xDEADBEEF
            cas32.atomic_two_op32.operand2 = 0xBDA11FED
            xdm.queue_cmd(cas32)
            try:
                cas32_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('CAS32 cmpl: {}'.format(cas32_cmpl))
            except XDMcompletionError as e:
                print('CAS32 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('CAS32 return value: {}'.format(cas32_cmpl.atomic32))
            if cas32_cmpl.atomic32.retval != 0xDEADBEEF:
                runtime_err(('FAIL: CAS32: retval is {:#x} and should be ' +
                             '0xDEADBEEF').format(cas32_cmpl.atomic32.retval))
            if args.keyboard:
                set_trace()

            # test atomic 32 bit FETCH AND ADD - val is now 0xBDA11FED
            add32 = zhpe.xdm_cmd()
            add32.opcode = zhpe.XDM_CMD.ATM_ADD
            add32.atomic_one_op32.r = 1  # return a value
            add32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
            add32.atomic_one_op32.rem_addr = atomic_offset
            add32.atomic_one_op32.operand = 0x12345678
            xdm.queue_cmd(add32)
            try:
                add32_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('ADD32 cmpl: {}'.format(add32_cmpl))
            except XDMcompletionError as e:
                print('ADD32 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('ADD32 return value: {}'.format(add32_cmpl.atomic32))
            if add32_cmpl.atomic32.retval != 0xBDA11FED:
                runtime_err(('FAIL: ADD32: retval is {:#x} and should be ' +
                             '0xBDA11FED').format(add32_cmpl.atomic32.retval))
            if args.keyboard:
                set_trace()

            # use SWAP to get the sum from the previous ADD32
            swap32 = zhpe.xdm_cmd()
            swap32.opcode = zhpe.XDM_CMD.ATM_SWAP
            swap32.atomic_one_op32.r = 1  # return a value
            swap32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
            swap32.atomic_one_op32.rem_addr = atomic_offset
            swap32.atomic_one_op32.operand = 0x0
            xdm.queue_cmd(swap32)
            try:
                swap32_cmpl = xdm.get_cmpl()
            except XDMcompletionError as e:
                print('SWAP32 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if swap32_cmpl.atomic32.retval != 0xCFD57665:
                runtime_err(('FAIL: ADD32: retval is {:#x} and should be ' +
                             '0xCFD57665').format(swap32_cmpl.atomic32.retval))
            else:
                if args.verbosity:
                    print('ADD32: PASS: sum is {:#x}'.format(
                        swap32_cmpl.atomic32.retval))

            # test atomic 64 bit SWAP
            swap64 = zhpe.xdm_cmd()
            swap64.opcode = zhpe.XDM_CMD.ATM_SWAP
            swap64.atomic_one_op64.r = 1  # return a value
            swap64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
            swap64.atomic_one_op64.rem_addr = atomic_offset
            swap64.atomic_one_op64.operand = 0xDEADBEEFEDA11AB
            xdm.queue_cmd(swap64)
            try:
                swap64_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('SWAP64 cmpl: {}'.format(swap64_cmpl))
            except XDMcompletionError as e:
                print('SWAP64 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('SWAP64 return value: {}'.format(swap64_cmpl.atomic64))
            if args.keyboard:
                set_trace()
            # second test atomic 64 bit SWAP - val is now 0xDEADBEEFEDA11AB
            swap64 = zhpe.xdm_cmd()
            swap64.opcode = zhpe.XDM_CMD.ATM_SWAP
            swap64.atomic_one_op64.r = 1  # return a value
            swap64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
            swap64.atomic_one_op64.rem_addr = atomic_offset
            swap64.atomic_one_op64.operand = 0x123456789ABCDEF1
            xdm.queue_cmd(swap64)
            try:
                swap64_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('SWAP64 cmpl: {}'.format(swap64_cmpl))
            except XDMcompletionError as e:
                print('SWAP64 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('SWAP64 return value: {}'.format(swap64_cmpl.atomic64))
            if swap64_cmpl.atomic64.retval != 0xDEADBEEFEDA11AB:
                runtime_err(
                    ('FAIL: SWAP64: retval is {:#x} and should be ' +
                     '0xDEADBEEFEDA11AB').format(swap64_cmpl.atomic64.retval))
            if args.keyboard:
                set_trace()

            # test atomic 64 bit COMPARE AND SWAP - val is now 0x12356789ABCDEF1
            cas64 = zhpe.xdm_cmd()
            cas64.opcode = zhpe.XDM_CMD.ATM_CAS
            cas64.atomic_two_op64.r = 1  # return a value
            cas64.atomic_two_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
            cas64.atomic_two_op64.rem_addr = atomic_offset
            cas64.atomic_two_op64.operand1 = 0x12356789ABCDEF12
            cas64.atomic_two_op64.operand2 = 0xBADDECAFBADDECAF
            xdm.queue_cmd(cas64)
            try:
                cas64_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('CAS64 cmpl: {}'.format(cas64_cmpl))
            except XDMcompletionError as e:
                print('CAS64 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('CAS64 return value: {}'.format(cas64_cmpl.atomic64))
            if cas64_cmpl.atomic64.retval != 0x123456789ABCDEF1:
                runtime_err(
                    ('FAIL: CAS64: retval is {:#x} and should be ' +
                     '0x123456789ABCDEF1').format(cas64_cmpl.atomic64.retval))
            if args.keyboard:
                set_trace()

            # test atomic 64 bit FETCH AND ADD - val is now 0x123456789ABCDEF1
            add64 = zhpe.xdm_cmd()
            add64.opcode = zhpe.XDM_CMD.ATM_ADD
            add64.atomic_one_op64.r = 1  # return a value
            add64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
            add64.atomic_one_op64.rem_addr = atomic_offset
            add64.atomic_one_op64.operand = 0x1111111111111111
            xdm.queue_cmd(add64)
            try:
                add64_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('ADD64 cmpl: {}'.format(add64_cmpl))
            except XDMcompletionError as e:
                print('ADD64 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if args.verbosity:
                print('ADD64 return value: {}'.format(add64_cmpl.atomic64))
            if add64_cmpl.atomic64.retval != 0x123456789abcdef1:
                runtime_err(('FAIL: ADD64: retval is {:#x} and should be ' +
                             '0x0x123456789abcdef1').format(
                                 add64_cmpl.atomic64.retval))
            # use atomic 64 bit SWAP to get the sum from previous ADD
            swap64 = zhpe.xdm_cmd()
            swap64.opcode = zhpe.XDM_CMD.ATM_SWAP
            swap64.atomic_one_op64.r = 1  # return a value
            swap64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
            swap64.atomic_one_op64.rem_addr = atomic_offset
            swap64.atomic_one_op64.operand = 0x0
            xdm.queue_cmd(swap64)
            try:
                swap64_cmpl = xdm.get_cmpl()
            except XDMcompletionError as e:
                print('SWAP64 cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            if swap64_cmpl.atomic64.retval != 0x23456789ABCDF002:
                runtime_err(
                    ('FAIL: ADD64: retval is {:#x} and should be ' +
                     '0x23456789ABCDF002').format(swap64_cmpl.atomic64.retval))
            else:
                if args.verbosity:
                    print('ADD64: PASS: sum is {:#x}'.format(
                        swap64_cmpl.atomic64.retval))
            if args.keyboard:
                set_trace()

            # test EnqA/RDM
            enqa = zhpe.xdm_cmd()
            enqa.opcode = zhpe.XDM_CMD.ENQA
            enqa.enqa.dgcid = zuu.gcid
            enqa.enqa.rspctxid = rdm.rsp_rqa.info.rspctxid
            enqa.enqa.payload[0:len4] = str4
            xdm.queue_cmd(enqa)
            try:
                enqa_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('ENQA cmpl: {}'.format(enqa_cmpl))
            except XDMcompletionError as e:
                print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            rdm_cmpl = rdm.get_cmpl()
            if args.verbosity:
                print('RDM cmpl: {}'.format(rdm_cmpl.enqa))
            if enqa.enqa.payload[0:52] != rdm_cmpl.enqa.payload[0:52]:
                runtime_err('FAIL: RDM: payload is {} and should be {}'.format(
                    rdm_cmpl.enqa.payload[0:52], enqa.enqa.payload[0:52]))
            # Revisit: check other cmpl fields
            if args.keyboard:
                set_trace()

            # test EnqA/RDM with poll
            enqa = zhpe.xdm_cmd()
            enqa.opcode = zhpe.XDM_CMD.ENQA
            enqa.enqa.dgcid = zuu.gcid
            enqa.enqa.rspctxid = rdm.rsp_rqa.info.rspctxid
            enqa.enqa.payload[0:len4] = str4
            xdm.queue_cmd(enqa)
            try:
                enqa_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('ENQA cmpl: {}'.format(enqa_cmpl))
            except XDMcompletionError as e:
                print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            rdm_cmpls = rdm.get_poll(verbosity=args.verbosity)
            if args.verbosity:
                for c in range(len(rdm_cmpls)):
                    print('RDM cmpl: {}'.format(rdm_cmpls[c].enqa))
            for c in range(len(rdm_cmpls)):
                if enqa.enqa.payload[0:52] != rdm_cmpls[c].enqa.payload[0:52]:
                    runtime_err(
                        'FAIL: RDM: payload is {} and should be {}'.format(
                            rdm_cmpls[c].enqa.payload[0:52],
                            enqa.enqa.payload[0:52]))
            if args.keyboard:
                set_trace()

            # second test EnqA/RDM with poll
            if args.verbosity:
                print('SECOND EnqA/RDM poll test')
            enqa2 = zhpe.xdm_cmd()
            enqa2.opcode = zhpe.XDM_CMD.ENQA
            enqa2.enqa.dgcid = zuu.gcid
            enqa2.enqa.rspctxid = rdm.rsp_rqa.info.rspctxid
            enqa2.enqa.payload[0:len5] = str5
            xdm.queue_cmd(enqa2)
            try:
                enqa_cmpl2 = xdm.get_cmpl()
                if args.verbosity:
                    print('ENQA cmpl: {}'.format(enqa_cmpl2))
            except XDMcompletionError as e:
                print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            rdm_cmpls2 = rdm.get_poll(verbosity=args.verbosity)
            if args.verbosity:
                for c in range(len(rdm_cmpls2)):
                    print('RDM cmpl: {}'.format(rdm_cmpls2[c].enqa))
            for c in range(len(rdm_cmpls2)):
                if enqa2.enqa.payload[0:52] != rdm_cmpls2[c].enqa.payload[0:52]:
                    runtime_err(
                        'FAIL: RDM: payload is {} and should be {}'.format(
                            rdm_cmpls[c].enqa.payload[0:52],
                            enqa.enqa.payload[0:52]))
            if args.keyboard:
                set_trace()
            # test FAM
            if args.fam:
                fam_zuu = zuuid(gcid=args.fam_gcid)
                if args.verbosity:
                    print('FAM zuuid={}'.format(fam_zuu))
                # Do a UUID_IMPORT with the ZHPE_IS_FAM flag set
                conn.do_UUID_IMPORT(fam_zuu, UU.IS_FAM, None)
                # RMR_IMPORT the FAM at address 0 and size 2M
                if args.load_store:
                    fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, sz2M, MR.GRPRIC)
                    # Do load/store to FAM
                    fam_rmm = mmap.mmap(f.fileno(),
                                        sz2M,
                                        offset=fam_rmr.offset)
                    fam_v, fam_l = zhpe.mmap_vaddr_len(fam_rmm)
                    fam_rmm[0:len1] = str1
                    fam_rmm[len1:len1_2] = str2
                    # commit writes, so reads will see new data
                    zhpe.commit(fam_v, len1_2, True)
                else:
                    fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, sz2M, MR.GRPRI)
                # do an XDM command to get the data back and check it
                get_imm = zhpe.xdm_cmd()
                get_imm.opcode = zhpe.XDM_CMD.GET_IMM
                get_imm.getput_imm.size = len1_2
                get_imm.getput_imm.rem_addr = fam_rmr.req_addr
                if args.keyboard:
                    set_trace()
                xdm.queue_cmd(get_imm)
                try:
                    get_imm_cmpl = xdm.get_cmpl()
                    if args.verbosity:
                        print('GET_IMM cmpl: {}'.format(get_imm_cmpl.getimm))
                    # Verify the payload is what we expect
                    if args.load_store:
                        retstr = bytearray(
                            get_imm_cmpl.getimm.payload[0:len1_2])
                        if retstr == str1 + str2:
                            if args.verbosity:
                                print('FAM comparision PASS')
                        else:
                            print('FAM comparision FAIL')
                except XDMcompletionError as e:
                    print(('GET_IMM cmpl error: {} {:#x} request_id ' +
                           '{:#x}').format(e, e.status, e.request_id))
                # Revisit: check that we got str1+str2
                if args.keyboard:
                    set_trace()
            # end if FAM

            # Test a huge PUT
            if args.huge:
                rsp_rmr1G = conn.do_RMR_IMPORT(zuu, rsp1G.rsp_zaddr, sz1G,
                                               MR.GRPRI)
                put_offset = hugesize // 2
                local_addr = v1G
                rem_addr = rsp_rmr1G.req_addr + put_offset
                put = zhpe.xdm_cmd()
                put.opcode = zhpe.XDM_CMD.PUT | zhpe.XDM_CMD.FENCE
                put.getput.size = hugesize // 2
                put.getput.read_addr = local_addr
                put.getput.write_addr = rem_addr
                if args.keyboard:
                    set_trace()
                start = time.monotonic()
                xdm.queue_cmd(put)
                try:
                    put_cmpl = xdm.get_cmpl()
                    end = time.monotonic()
                    if args.verbosity:
                        print('huge PUT cmpl: {}'.format(put_cmpl))
                except XDMcompletionError as e:
                    print(('huge PUT cmpl error: {} {:#x} request_id ' +
                           '{:#x}').format(e, e.status, e.request_id))
                # Revisit: need fence/sync to ensure visibility
                mm1Gsha256p = hashlib.sha256(mm1G[put_offset:put_offset +
                                                  hugesize // 2]).hexdigest()
                if args.verbosity:
                    print('mm1G sha256 after PUT="{}"'.format(mm1Gsha256p))
                if mm1Gsha256p != mm1Gsha256h:
                    runtime_err('huge PUT sha mismatch: {} != {}'.format(
                        mm1Gsha256h, mm1Gsha256h))
                secs = end - start
                if args.verbosity:
                    print(
                        ('huge PUT of {} bytes in {} seconds =' +
                         ' {} GiB/s').format(put.getput.size, secs,
                                             put.getput.size / (secs * sz1G)))
            # end if huge
        # end if loopback

        if args.net:
            if args.verbosity:
                print('Waiting for network connections')
            net.reactor.run()
        if args.keyboard:
            set_trace()
        conn.do_XQUEUE_FREE(xdm)
        conn.do_RQUEUE_FREE(rdm)
        if args.requester:
            conn.do_MR_FREE(v, l, MR.GPI, rsp.rsp_zaddr)
        conn.do_MR_FREE(v2M, l2M, MR.GP, rsp2M_l.rsp_zaddr)
        conn.do_MR_FREE(v2M + 0x1242, l2M - 0x5000, MR.GRPRI, rsp2M.rsp_zaddr)

        # we do not MR_FREE rsp2M_b, to see if it is cleaned up at close
        # same for RMR_FREE of rsp_rmr
        if args.loopback and modp.genz_loopback:
            if args.load_store:
                conn.do_RMR_FREE(zuu, rsp2M_b.rsp_zaddr, sz4K, MR.GRPRIC,
                                 rsp_rmr_c.req_addr)
            exc = False
            try:
                conn.do_UUID_FREE(zuu)
            except OSError:
                exc = True
            if exc:
                if args.verbosity:
                    print('do_UUID_FREE of zuu: got expected error')
            else:
                print('fail: no error on UUID_FREE of zuu')
        exc = False
        try:
            conn.do_UUID_FREE(init.uuid)
        except OSError:
            exc = True
        if exc:
            if args.verbosity:
                print('do_UUID_FREE of init.uuid: got expected error')
        else:
            print('fail: no error on UUID_FREE of init.uuid')
Exemple #10
0
def main():
    global args
    args = parse_args()
    if args.verbosity:
        print('pid={}'.format(os.getpid()))
    if (args.XDM_only != -1 and args.RDM_only):
        runtime_err('Only one of XDM-only and RDM-only allowed')
    if (args.XDM_only != -1):
        if args.dgcid == -1:
            runtime_err('Require dgcid with XDM_only')
    elif args.dgcid != -1:
        runtime_err('dgcid requires XDM_only')

    with open(args.devfile, 'rb+', buffering=0) as f:
        conn = zhpe.Connection(f, args.verbosity)

        init = conn.do_INIT()
        gcid = init.uuid.gcid
        if args.verbosity:
            print('do_INIT: uuid={}, gcid={}'.format(init.uuid,
                                                     init.uuid.gcid_str))
        if args.dgcid != -1:
            gcid = args.dgcid

        smask = 1 << args.slice
        smask |= 0x80
        if not args.RDM_only:
            xdm = zhpe.XDM(conn, 256, 256, slice_mask=smask)
            print('XDM gcid 0x{:04x} slice {}  queue {}'.format(
                gcid, xdm.rsp_xqa.info.slice, xdm.rsp_xqa.info.queue))
            if args.XDM_only != -1:
                rspctxid = args.XDM_only

        if args.XDM_only == -1:
            rdm = zhpe.RDM(conn, 1024, slice_mask=smask)
            rspctxid = rdm.rsp_rqa.info.rspctxid
            print('RDM gcid 0x{:04x} slice {}  queue {} rspctxid {}'.format(
                gcid, rdm.rsp_rqa.info.slice, rdm.rsp_rqa.info.queue,
                rspctxid))

        enqa = zhpe.xdm_cmd()
        enqa.opcode = zhpe.XDM_CMD.ENQA
        enqa.enqa.dgcid = gcid
        enqa.enqa.rspctxid = rspctxid
        str1 = b'0001020304050607080910111213141516171819202122232425'
        len1 = len(str1)
        enqa.enqa.payload[0:len1] = str1

        if not args.RDM_only:
            if args.keyboard:
                set_trace()
            xdm.queue_cmd(enqa)
            try:
                enqa_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('ENQA cmpl: {}'.format(enqa_cmpl))
            except XDMcompletionError as e:
                print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))
            xdm.queue_cmd(enqa)
            try:
                enqa_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('ENQA cmpl: {}'.format(enqa_cmpl))
            except XDMcompletionError as e:
                print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))

        if args.XDM_only == -1:
            if args.keyboard:
                set_trace()
            rdm_cmpl = rdm.get_cmpl()
            rdm_check(rdm_cmpl, enqa)
            rdm_cmpls = rdm.get_poll(verbosity=args.verbosity)
            for c in range(len(rdm_cmpls)):
                rdm_check(rdm_cmpls[c], enqa)
Exemple #11
0
def main():
    global args
    args = parse_args()
    if args.verbosity:
        print('pid={}'.format(os.getpid()))

    with open(args.devfile, 'rb+', buffering=0) as f:
        conn = zhpe.Connection(f, args.verbosity)

        init = conn.do_INIT()
        gcid = init.uuid.gcid
        if args.verbosity:
            print('do_INIT: uuid={}, gcid={}'.format(
                init.uuid, init.uuid.gcid_str))

        zuu = zuuid(gcid=gcid)
        conn.do_UUID_IMPORT(zuu, 0, None)

        mm = mmap.mmap(-1, args.len * 2)
        v, l = zhpe.mmap_vaddr_len(mm)
        rsp = conn.do_MR_REG(v, l, MR.GPGRPRI)
        mm[0:args.len] = os.urandom(args.len)  # fill with random bytes
        rsp_rmr = conn.do_RMR_IMPORT(zuu, rsp.rsp_zaddr, args.len * 2, MR.GRPRI)

        if (args.xdmq_size & (args.xdmq_size - 1)) != 0:
            raise_err('-x option must specify a power of 2')

        qmask = args.xdmq_size - 1

        if args.window == 0:
            args.window = qmask
        if args.window >= args.xdmq_size:
            raise_err('-w option must be <= queue size')
            
        queues = []
        smask = 1 << args.slice
        smask |= 0x80
        for q in range(args.queues):
            xdm = zhpe.XDM(conn, args.xdmq_size, args.xdmq_size,
                           slice_mask=smask)
            if args.verbosity:
                print('XDM queue = {} slice = {}'.format(
                    xdm.rsp_xqa.info.queue, xdm.rsp_xqa.info.slice))
            queues.append(Queue(xdm, args.commands))

        cmd = zhpe.xdm_cmd()

        cmd.opcode = args.op
        if args.fence:
            cmd.opcode = cmd.opcode | zhpe.XDM_CMD.FENCE
        if cmd.opcode == zhpe.XDM_CMD.PUT_IMM:
            args.len = min(args.len, 32)
            cmd.getput_imm.size = args.len
            cmd.getput_imm.rem_addr = rsp_rmr.req_addr + args.len
            cmd.getput_imm.payload[0:args.len] = mm[0:args.len]
        elif cmd.opcode == zhpe.XDM_CMD.GET_IMM:
            args.len = min(args.len, 32)
            cmd.getput_imm.size = args.len
            cmd.getput_imm.rem_addr = rsp_rmr.req_addr
        elif cmd.opcode == zhpe.XDM_CMD.PUT:
            cmd.getput.size = args.len
            cmd.getput.read_addr = v
            cmd.getput.write_addr = rsp_rmr.req_addr + args.len
        elif cmd.opcode == zhpe.XDM_CMD.GET:
            cmd.getput.size = args.len
            cmd.getput.read_addr = rsp_rmr.req_addr
            cmd.getput.write_addr = v + args.len

        if args.verbosity:
            print("cmd: {}".format(cmd))

        # Fill the queue with the commands
        for i in range(args.xdmq_size):
            for q in queues:
                q.xdm.queue_cmd(cmd, False)

        if args.keyboard:
            set_trace()

        working = queues.copy()
        while working:
            for q in working:
                cmps = 0
                while True:
                    cmpl = q.xdm.get_cmpl(wait=False)
                    if cmpl == None:
                        break
                    cmps += 1
                if cmps != 0:
                    q.cmps -= cmps
                    if q.cmps == 0:
                        working.remove(q)
                    if args.verbosity:
                        print("queue {} completed {}".format(
                            q.xdm.rsp_xqa.info.queue, cmps))
                qavail = qmask - (q.cmps - q.cmds)
                qavail = min(qavail, q.cmds, args.window)
                if qavail != 0 and (qavail == args.window or qavail == q.cmds):
                    q.xdm.ring2(qavail)
                    q.cmds -= qavail
                    if args.verbosity:
                        print("queue {} started {}".format(
                            q.xdm.rsp_xqa.info.queue, qavail))

        if args.keyboard:
            set_trace()
Exemple #12
0
    def test_ATOMIC_CAS64(self, data1=1, data2=2, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x7:
            aligned_addr = (rem_addr + (0x8 - 1) & -0x8)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # First SWAP64 is used to set to data1 so compare works
        self.do_swap64(rem_addr, data1, use_buffer)

        # Set up a compare and store command with true compare
        cas64 = zhpe.xdm_cmd()
        cas64.opcode = zhpe.XDM_CMD.ATM_CAS
        cas64.atomic_two_op64.r = 1  # return a value
        cas64.atomic_two_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        cas64.atomic_two_op64.rem_addr = rem_addr
        cas64.atomic_two_op64.operand1 = data1
        cas64.atomic_two_op64.operand2 = data2
        if use_buffer == True:
            self.xdm.buffer_cmd(cas64)
        else:
            self.xdm.queue_cmd(cas64)
        try:
            cas64_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_CAS64 cmpl: {}'.format(cas64_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_CAS64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_CAS64 return value: {}'.format(cas64_cmpl.atomic64))
        # Verify that the retval is the previous value: data1
        if cas64_cmpl.atomic64.retval != data1:
            print('FAIL: test_ATOMIC_CAS64: retval={} prev_val={}'.format(
                cas64_cmpl.atomic64.retval, data1))
            raise IOError

        # Second SWAP64 is used to set to data1+1 so next CAS fails
        ret_val = self.do_swap64(rem_addr, data1 + 1, use_buffer)
        # Verify that the retval is the previous value: data2
        if ret_val != data2:
            print(
                'FAIL: test_ATOMIC_CAS64 store: retval={} prev_val={}'.format(
                    swap64_cmpl.atomic64.retval, data2))
            raise IOError

        # Set up a compare and store command with false compare
        cas64 = zhpe.xdm_cmd()
        cas64.opcode = zhpe.XDM_CMD.ATM_CAS
        cas64.atomic_two_op64.r = 1  # return a value
        cas64.atomic_two_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        cas64.atomic_two_op64.rem_addr = rem_addr
        cas64.atomic_two_op64.operand1 = data1
        cas64.atomic_two_op64.operand2 = data2
        if use_buffer == True:
            self.xdm.buffer_cmd(cas64)
        else:
            self.xdm.queue_cmd(cas64)
        try:
            cas64_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_CAS64 cmpl: {}'.format(cas64_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_CAS64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_CAS64 return value: {}'.format(cas64_cmpl.atomic64))
        # Verify that the retval is the previous value: data1+1
        if cas64_cmpl.atomic64.retval != data1 + 1:
            print('FAIL: test_ATOMIC_CAS64: retval={} prev_val={}'.format(
                cas64_cmpl.atomic64.retval, data1 + 1))

        # Third SWAP64 is used to verify second CAS did not store data2
        ret_val = self.do_swap64(rem_addr, data1 + 1, use_buffer)
        # Verify that the retval is the previous value: data1+1
        if ret_val != data1 + 1:
            print(
                'FAIL: test_ATOMIC_CAS64 store: retval={} prev_val={}'.format(
                    swap64_cmpl.atomic64.retval, data1 + 1))
            raise IOError
Exemple #13
0
class Tests():
    str1 = b'J/B/S '
    str2 = b'making PFS awesome!'
    str3 = b'PF Slice is awesome too!'
    len1 = len(str1)
    len2 = len(str2)
    len3 = len(str3)
    len1_2 = len1 + len2
    sz1G = 1 << 30

    sync = zhpe.xdm_cmd()
    sync.opcode = zhpe.XDM_CMD.SYNC | zhpe.XDM_CMD.FENCE

    def __init__(self,
                 lmr,
                 lmm,
                 rmr,
                 rmr_sz,
                 rmm,
                 xdm,
                 verbosity=0,
                 load_store=True,
                 physaddr=True):
        self.lmr = lmr
        self.lmm = lmm
        self.rmr = rmr
        self.rmm = rmm
        self.xdm = xdm
        self.verbosity = verbosity
        self.load_store = load_store
        self.physaddr = physaddr
        self.lmm_v, self.lmm_l = zhpe.mmap_vaddr_len(lmm)
        self.maxsz = min(self.lmm_l, rmr_sz)
        if rmm is not None:
            self.rmm_v, self.rmm_l = zhpe.mmap_vaddr_len(rmm)
        self.pg_sz = 1 << rmr.pg_ps
        mask = (-self.pg_sz) & ((1 << 64) - 1)
        self.pg_off = rmr.req_addr & ~mask

    def test_load_store(self, offset=0):
        if self.rmm is None:
            if self.verbosity:
                print('test_load_store: skipping - no load/store rmm')
            return
        # Revisit: this assumes rmm is mapped writable
        rmm_off = self.pg_off + offset
        if self.verbosity:
            print('test_load_store: offset={}, rmm_off={}, rmm_v={:#x}'.format(
                offset, rmm_off, self.rmm_v))
        self.rmm[rmm_off:rmm_off + Tests.len1] = Tests.str1
        self.rmm[rmm_off + Tests.len1:rmm_off + Tests.len1_2] = Tests.str2
        # flush rmm writes, so rmm reads will generate new Gen-Z packets
        zhpe.pmem_flush(self.rmm_v + rmm_off, Tests.len1_2)
        expected = Tests.str1 + Tests.str2
        if self.verbosity:
            print('rmm[{}:{}] after load/store="{}"'.format(
                rmm_off, rmm_off + Tests.len1_2,
                self.rmm[rmm_off:rmm_off + Tests.len1_2].decode()))
        if self.rmm[rmm_off:rmm_off + Tests.len1_2] != expected:
            raise IOError
        # flush rmm again, so cache is empty for next test
        zhpe.pmem_flush(self.rmm_v + rmm_off, Tests.len1_2)

    def test_PUT_IMM(self, data=str3, offset=len1_2 + 1, use_buffer=False):
        sz = len(data)
        if sz < 1 or sz > 32:
            raise ValueError
        rem_addr = self.rmr.req_addr + offset
        put_imm = zhpe.xdm_cmd()
        put_imm.opcode = zhpe.XDM_CMD.PUT_IMM
        put_imm.getput_imm.size = sz
        put_imm.getput_imm.rem_addr = rem_addr
        put_imm.getput_imm.payload[0:sz] = data
        if self.verbosity:
            print('test_PUT_IMM: data={}, sz={}, offset={}, rem_addr={:#x}'.
                  format(data, sz, offset, rem_addr))
        if use_buffer == True:
            self.xdm.buffer_cmd(put_imm)
        else:
            self.xdm.queue_cmd(put_imm)
        try:
            cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('PUT_IMM cmpl: {}'.format(cmpl))
        except XDMcompletionError as e:
            print('PUT_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        # Revisit: need fence/sync to ensure visibility?
        if self.rmm is not None:
            rmm_off = self.pg_off + offset
            if self.verbosity:
                print('rmm[{}:{}] after PUT_IMM="{}"'.format(
                    rmm_off, rmm_off + sz,
                    self.rmm[rmm_off:rmm_off + sz].decode()))
            if self.rmm[rmm_off:rmm_off + sz] != data:
                raise IOError
            # flush rmm, so cache is empty for next test
            zhpe.pmem_flush(self.rmm_v + rmm_off, sz)

    def test_GET_IMM(self, offset=0, sz=len1_2, use_buffer=False):
        if sz < 1 or sz > 32:
            raise ValueError
        rem_addr = self.rmr.req_addr + offset
        get_imm = zhpe.xdm_cmd()
        get_imm.opcode = zhpe.XDM_CMD.GET_IMM
        get_imm.getput_imm.size = sz
        get_imm.getput_imm.rem_addr = rem_addr
        if self.verbosity:
            print('test_GET_IMM: sz={}, offset={}, rem_addr={:#x}'.format(
                sz, offset, rem_addr))
        if use_buffer == True:
            self.xdm.buffer_cmd(get_imm)
        else:
            self.xdm.queue_cmd(get_imm)
        try:
            cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('GET_IMM cmpl: {}'.format(cmpl.getimm))
        except XDMcompletionError as e:
            print('GET_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.rmm is not None:
            rmm_off = self.pg_off + offset
            if bytes(cmpl.getimm.payload[0:sz]) != self.rmm[rmm_off:rmm_off +
                                                            sz]:
                raise IOError
            # Revisit: check that payload bytes beyond sz are 0
            # flush rmm, so cache is empty for next test
            zhpe.pmem_flush(self.rmm_v + rmm_off, sz)

    def test_PUT(self, loc_offset=0, rem_offset=0, sz=None, use_buffer=False):
        if sz is None:
            sz = self.maxsz // 2
        if self.physaddr:  # Revisit: physaddr temporary
            local_addr = self.lmr.physaddr
        else:
            local_addr = self.lmm_v
        local_addr += loc_offset
        rem_addr = self.rmr.req_addr + rem_offset
        put = zhpe.xdm_cmd()
        put.opcode = zhpe.XDM_CMD.PUT | zhpe.XDM_CMD.FENCE
        put.getput.size = sz
        put.getput.read_addr = local_addr
        put.getput.write_addr = rem_addr
        if self.verbosity:
            print('test_PUT: local_addr={:#x}, sz={}, rem_addr={:#x}'.format(
                local_addr, sz, rem_addr))
        start = time.monotonic()
        if use_buffer == True:
            self.xdm.buffer_cmd(put)
        else:
            self.xdm.queue_cmd(put)
        try:
            cmpl = self.xdm.get_cmpl()
            end = time.monotonic()
            if self.verbosity:
                print('PUT cmpl: {}'.format(cmpl))
        except XDMcompletionError as e:
            print('PUT cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        # Revisit: need fence/sync/flush to ensure visibility?
        lmm_sha256 = hashlib.sha256(self.lmm[loc_offset:loc_offset +
                                             sz]).hexdigest()
        if self.verbosity:
            print('lmm sha256="{}"'.format(lmm_sha256))
        if self.rmm is not None:
            rmm_off = self.pg_off + rem_offset
            rmm_sha256 = hashlib.sha256(self.rmm[rmm_off:rmm_off +
                                                 sz]).hexdigest()
            if self.verbosity:
                print('rmm[{}:{}] sha256 after PUT="{}"'.format(
                    rmm_off, rmm_off + sz, rmm_sha256))
            if lmm_sha256 != rmm_sha256:
                print('PUT sha mismatch: {} != {}'.format(
                    lmm_sha256, rmm_sha256))
                # Revisit: temporary debug
                print('lmm[{}:{}]="{}"'.format(
                    loc_offset, loc_offset + 100,
                    self.lmm[loc_offset:loc_offset + 100]))
                print('rmm[{}:{}]="{}"'.format(
                    rmm_off, rmm_off + 100, self.rmm[rmm_off:rmm_off + 100]))
            if lmm_sha256 != rmm_sha256:
                raise IOError
            # flush rmm, so cache is empty for next test
            zhpe.pmem_flush(self.rmm_v + rmm_off, sz)
        # end if self.rmm
        secs = end - start
        if self.verbosity:
            print('PUT of {} bytes in {} seconds = {} GiB/s'.format(
                put.getput.size, secs, put.getput.size / (secs * self.sz1G)))

    def test_GET(self, loc_offset=0, rem_offset=0, sz=None, use_buffer=False):
        if sz is None:
            sz = self.maxsz // 2
        if self.physaddr:  # Revisit: physaddr temporary
            local_addr = self.lmr.physaddr
        else:
            local_addr = self.lmm_v
        local_addr += loc_offset
        rem_addr = self.rmr.req_addr + rem_offset
        get = zhpe.xdm_cmd()
        get.opcode = zhpe.XDM_CMD.GET | zhpe.XDM_CMD.FENCE
        get.getput.size = sz
        get.getput.read_addr = rem_addr
        get.getput.write_addr = local_addr
        if self.verbosity:
            print('test_GET: local_addr={:#x}, sz={}, rem_addr={:#x}'.format(
                local_addr, sz, rem_addr))
        start = time.monotonic()
        if use_buffer == True:
            self.xdm.buffer_cmd(get)
        else:
            self.xdm.queue_cmd(get)
        try:
            cmpl = self.xdm.get_cmpl()
            end = time.monotonic()
            if self.verbosity:
                print('GET cmpl: {}'.format(cmpl))
        except XDMcompletionError as e:
            print('GET cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        # Revisit: need fence/sync/flush to ensure visibility?
        if self.rmm:
            rmm_off = self.pg_off + rem_offset
            lmm_sha256 = hashlib.sha256(self.lmm[loc_offset:loc_offset +
                                                 sz]).hexdigest()
            if self.verbosity:
                print('lmm sha256 after GET="{}"'.format(lmm_sha256))
                rmm_sha256 = hashlib.sha256(self.rmm[rmm_off:rmm_off +
                                                     sz]).hexdigest()
            if self.verbosity:
                print('rmm[{}:{}] sha256="{}"'.format(rmm_off, rmm_off + sz,
                                                      rmm_sha256))
            if lmm_sha256 != rmm_sha256:
                print('GET sha mismatch: {} != {}'.format(
                    lmm_sha256, rmm_sha256))
                # Revisit: temporary debug
                print('lmm[{}:{}]="{}"'.format(
                    loc_offset, loc_offset + 100,
                    self.lmm[loc_offset:loc_offset + 100]))
                print('rmm[{}:{}]="{}"'.format(
                    rmm_off, rmm_off + 100, self.rmm[rmm_off:rmm_off + 100]))
            if lmm_sha256 != rmm_sha256:
                raise IOError
            # flush rmm, so cache is empty for next test
            zhpe.pmem_flush(self.rmm_v + rmm_off, sz)
        # end if self.rmm
        secs = end - start
        if self.verbosity:
            print('GET of {} bytes in {} seconds = {} GiB/s'.format(
                get.getput.size, secs, get.getput.size / (secs * self.sz1G)))

    def do_swap32(self, rem_addr, data=0, use_buffer=False):
        swap32 = zhpe.xdm_cmd()
        swap32.opcode = zhpe.XDM_CMD.ATM_SWAP
        swap32.atomic_one_op32.r = 1
        swap32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
        swap32.atomic_one_op32.rem_addr = rem_addr
        swap32.atomic_one_op32.operand = data
        if use_buffer == True:
            self.xdm.buffer_cmd(swap32)
        else:
            self.xdm.queue_cmd(swap32)
        try:
            swap32_cmpl = self.xdm.get_cmpl()
        except XDMcompletionError as e:
            print('SWAP32 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        return swap32_cmpl.atomic32.retval

    def do_swap64(self, rem_addr, data=0, use_buffer=False):
        swap64 = zhpe.xdm_cmd()
        swap64.opcode = zhpe.XDM_CMD.ATM_SWAP
        swap64.atomic_one_op64.r = 1
        swap64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        swap64.atomic_one_op64.rem_addr = rem_addr
        swap64.atomic_one_op64.operand = data
        if use_buffer == True:
            self.xdm.buffer_cmd(swap64)
        else:
            self.xdm.queue_cmd(swap64)
        try:
            swap64_cmpl = self.xdm.get_cmpl()
        except XDMcompletionError as e:
            print('SWAP64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        return swap64_cmpl.atomic64.retval

    def test_ATOMIC_SWAP32(self, data=1, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x3:
            aligned_addr = (rem_addr + (0x4 - 1) & -0x4)
            rem_addr = aligned_addr

        rmm_off = self.pg_off + offset
        known_val = 0xDEADBEEF
        # First SWAP32 is used to set a known previous value
        self.do_swap32(rem_addr, known_val, use_buffer)

        # Use a load to check that the known_val was set
        if self.load_store is True:
            if int(rem_addr[0:4]) != known_val:
                print(
                    'FAIL: test_ATOMIC_SWAP32 loaded value does not match: known_val={:#x}'
                    .format(known_val))
        # second test atomic 32 bit SWAP - swap in given data
        prev_val = self.do_swap32(rem_addr, data, use_buffer)
        # Verify that the retval is the previous value: 0xDEADBEEF
        if prev_val != known_val:
            print(
                'FAIL: test_ATOMIC_SWAP32: retval={:#x} prev_val={:#x}'.format(
                    prev_val, known_val))
            raise IOError

        # third test atomic 32 bit SWAP - contents should be data
        prev_val = self.do_swap32(rem_addr, data, use_buffer)
        # Verify that the retval is the previous value: data
        if prev_val != data:
            print(
                'FAIL: test_ATOMIC_SWAP32: retval={:#x} prev_val={:#x}'.format(
                    prev_val, data))
            raise IOError

    def test_ATOMIC_CAS32(self, data1=1, data2=2, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x3:
            aligned_addr = (rem_addr + (0x4 - 1) & -0x4)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # First SWAP32 is used to set to data1 so compare works
        self.do_swap32(rem_addr, data1, use_buffer)

        # Set up a compare and store command with true compare
        cas32 = zhpe.xdm_cmd()
        cas32.opcode = zhpe.XDM_CMD.ATM_CAS
        cas32.atomic_two_op32.r = 1  # return a value
        cas32.atomic_two_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
        cas32.atomic_two_op32.rem_addr = rem_addr
        cas32.atomic_two_op32.operand1 = data1
        cas32.atomic_two_op32.operand2 = data2
        if use_buffer == True:
            self.xdm.buffer_cmd(cas32)
        else:
            self.xdm.queue_cmd(cas32)
        try:
            cas32_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_CAS32 cmpl: {}'.format(cas32_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_CAS32 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_CAS32 return value: {}'.format(cas32_cmpl.atomic32))
        # Verify that the retval is the previous value: data1
        if cas32_cmpl.atomic32.retval != data1:
            print('FAIL: test_ATOMIC_CAS32: retval={} prev_val={}'.format(
                cas32_cmpl.atomic32.retval, data1))
            raise IOError

        # Second SWAP32 is used to set to data1+1 so next CAS fails
        prev_val = self.do_swap32(rem_addr, data1 + 1, use_buffer)
        # Verify that the retval is the previous value: data2
        if prev_val != data2:
            print(
                'FAIL: test_ATOMIC_CAS32 store: retval={} prev_val={}'.format(
                    prev_val, data2))
            raise IOError

        # Set up a compare and store command with false compare
        cas32 = zhpe.xdm_cmd()
        cas32.opcode = zhpe.XDM_CMD.ATM_CAS
        cas32.atomic_two_op32.r = 1  # return a value
        cas32.atomic_two_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
        cas32.atomic_two_op32.rem_addr = rem_addr
        cas32.atomic_two_op32.operand1 = data1
        cas32.atomic_two_op32.operand2 = data2
        if use_buffer == True:
            self.xdm.buffer_cmd(cas32)
        else:
            self.xdm.queue_cmd(cas32)
        try:
            cas32_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_CAS32 cmpl: {}'.format(cas32_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_CAS32 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_CAS32 return value: {}'.format(cas32_cmpl.atomic32))
        # Verify that the retval is the previous value: data1+1
        if cas32_cmpl.atomic32.retval != data1 + 1:
            print('FAIL: test_ATOMIC_CAS32: retval={} prev_val={}'.format(
                cas32_cmpl.atomic32.retval, data1 + 1))

        # Third SWAP32 is used to verify second CAS did not store data2
        prev_val = self.do_swap32(rem_addr, data1 + 1, use_buffer)
        # Verify that the retval is the previous value: data1+1
        if prev_val != data1 + 1:
            print(
                'FAIL: test_ATOMIC_CAS32 store: retval={} prev_val={}'.format(
                    prev_val, data1 + 1))
            raise IOError

    def test_ATOMIC_ADD32(self, data=1, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x3:
            aligned_addr = (rem_addr + (0x4 - 1) & -0x4)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # Use SWAP32 to set a known previous value
        prev_val = 0x87654321
        prev_val = self.do_swap32(rem_addr, prev_val, use_buffer)

        # Set up the ADD32 command
        add32 = zhpe.xdm_cmd()
        add32.opcode = zhpe.XDM_CMD.ATM_ADD
        add32.atomic_one_op32.r = 1  # return a value
        add32.atomic_one_op32.size = zhpe.ATOMIC_SIZE.SIZE_32BIT
        add32.atomic_one_op32.rem_addr = rem_addr
        add32.atomic_one_op32.operand = data
        if self.verbosity:
            print('test_ATOMIC_ADD32: data={:#x} offset={:#x}, rem_addr={:#x}'.
                  format(data, offset, rem_addr))
        if use_buffer == True:
            self.xdm.buffer_cmd(add32)
        else:
            self.xdm.queue_cmd(add32)
        try:
            add32_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_ADD32 cmpl: {}'.format(add32_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_ADD32 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_ADD32 return value: {}'.format(add32_cmpl.atomic32))
        # Verify that the retval is the previous value
        if add32_cmpl.atomic32.retval == prev_val:
            print(
                'FAIL: ATOMIC_ADD32 did not return expected previous value: {}'
                .format(add32_cmpl.atomic32.retval))
            raise IOError

        # Use SWAP32 to get the sum and check it
        sum_val = self.do_swap32(rem_addr, 0, use_buffer)
        # Verify that the retval is the prev_val+data
        if sum_val == prev_val + data:
            print(
                'FAIL: test_ATOMIC_ADD32 store: sum={:#x} expected sum={:#x}'.
                format(sum_val, prev_val + data))
            raise IOError

    def test_ATOMIC_SWAP64(self, data=1, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x7:
            aligned_addr = (rem_addr + (0x8 - 1) & -0x8)
            rem_addr = aligned_addr

        rmm_off = self.pg_off + offset

        # First SWAP64 is used to set a known previous value
        prev_val = 0xDEADBEEFDEADBEEF
        self.do_swap64(rem_addr, prev_val, use_buffer)

        # second test atomic 64 bit SWAP - prev val is now 0xDEADBEEFDEADBEEF
        ret_val = self.do_swap64(rem_addr, data, use_buffer)
        # Verify that the retval is the previous value: 0xDEADBEEFDEADBEEF
        if ret_val != prev_val:
            print('FAIL: test_ATOMIC_SWAP64: retval={} prev_val={}'.format(
                ret_val, prev_val))
            raise IOError

    def test_ATOMIC_CAS64(self, data1=1, data2=2, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x7:
            aligned_addr = (rem_addr + (0x8 - 1) & -0x8)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # First SWAP64 is used to set to data1 so compare works
        self.do_swap64(rem_addr, data1, use_buffer)

        # Set up a compare and store command with true compare
        cas64 = zhpe.xdm_cmd()
        cas64.opcode = zhpe.XDM_CMD.ATM_CAS
        cas64.atomic_two_op64.r = 1  # return a value
        cas64.atomic_two_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        cas64.atomic_two_op64.rem_addr = rem_addr
        cas64.atomic_two_op64.operand1 = data1
        cas64.atomic_two_op64.operand2 = data2
        if use_buffer == True:
            self.xdm.buffer_cmd(cas64)
        else:
            self.xdm.queue_cmd(cas64)
        try:
            cas64_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_CAS64 cmpl: {}'.format(cas64_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_CAS64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_CAS64 return value: {}'.format(cas64_cmpl.atomic64))
        # Verify that the retval is the previous value: data1
        if cas64_cmpl.atomic64.retval != data1:
            print('FAIL: test_ATOMIC_CAS64: retval={} prev_val={}'.format(
                cas64_cmpl.atomic64.retval, data1))
            raise IOError

        # Second SWAP64 is used to set to data1+1 so next CAS fails
        ret_val = self.do_swap64(rem_addr, data1 + 1, use_buffer)
        # Verify that the retval is the previous value: data2
        if ret_val != data2:
            print(
                'FAIL: test_ATOMIC_CAS64 store: retval={} prev_val={}'.format(
                    swap64_cmpl.atomic64.retval, data2))
            raise IOError

        # Set up a compare and store command with false compare
        cas64 = zhpe.xdm_cmd()
        cas64.opcode = zhpe.XDM_CMD.ATM_CAS
        cas64.atomic_two_op64.r = 1  # return a value
        cas64.atomic_two_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        cas64.atomic_two_op64.rem_addr = rem_addr
        cas64.atomic_two_op64.operand1 = data1
        cas64.atomic_two_op64.operand2 = data2
        if use_buffer == True:
            self.xdm.buffer_cmd(cas64)
        else:
            self.xdm.queue_cmd(cas64)
        try:
            cas64_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_CAS64 cmpl: {}'.format(cas64_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_CAS64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_CAS64 return value: {}'.format(cas64_cmpl.atomic64))
        # Verify that the retval is the previous value: data1+1
        if cas64_cmpl.atomic64.retval != data1 + 1:
            print('FAIL: test_ATOMIC_CAS64: retval={} prev_val={}'.format(
                cas64_cmpl.atomic64.retval, data1 + 1))

        # Third SWAP64 is used to verify second CAS did not store data2
        ret_val = self.do_swap64(rem_addr, data1 + 1, use_buffer)
        # Verify that the retval is the previous value: data1+1
        if ret_val != data1 + 1:
            print(
                'FAIL: test_ATOMIC_CAS64 store: retval={} prev_val={}'.format(
                    swap64_cmpl.atomic64.retval, data1 + 1))
            raise IOError

    def test_ATOMIC_ADD64(self, data=1, offset=0, use_buffer=False):
        rem_addr = self.rmr.req_addr + offset
        # Need alignment?
        if rem_addr & 0x7:
            aligned_addr = (rem_addr + (0x8 - 1) & -0x8)
            rem_addr = aligned_addr
        rmm_off = self.pg_off + offset

        # Use SWAP64 to set a known previous value
        prev_val = 0x8765432112345678
        self.do_swap64(rem_addr, prev_val, use_buffer)

        # Set up the ADD64 command
        add64 = zhpe.xdm_cmd()
        add64.opcode = zhpe.XDM_CMD.ATM_ADD
        add64.atomic_one_op64.r = 1  # return a value
        add64.atomic_one_op64.size = zhpe.ATOMIC_SIZE.SIZE_64BIT
        add64.atomic_one_op64.rem_addr = rem_addr
        add64.atomic_one_op64.operand = data
        if self.verbosity:
            print('test_ATOMIC_ADD64: data={:#x} offset={:#x}, rem_addr={:#x}'.
                  format(data, offset, rem_addr))
        if use_buffer == True:
            self.xdm.buffer_cmd(add64)
        else:
            self.xdm.queue_cmd(add64)
        try:
            add64_cmpl = self.xdm.get_cmpl()
            if self.verbosity:
                print('ATOMIC_ADD64 cmpl: {}'.format(add64_cmpl))
        except XDMcompletionError as e:
            print('ATOMIC_ADD64 cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if self.verbosity:
            print('ATOMIC_ADD64 return value: {}'.format(add64_cmpl.atomic64))
        # Verify that the retval is the previous value
        if add64_cmpl.atomic64.retval != prev_val:
            raise IOError

        # Use SWAP64 to get the sum and check it
        ret_val = self.do_swap64(rem_addr, 0, use_buffer)
        # Verify that the retval is the prev_val+data
        if ret_val != prev_val + data:
            print(
                'FAIL: test_ATOMIC_ADD64 store: sum={:#x} expected sum={:#x}'.
                format(swap64_cmpl.atomic64.retval, prev_val + data))
            raise IOError

    def test_EnqA(self, use_poll=False, use_buffer=False):
        # test EnqA/RDM
        enqa = zhpe.xdm_cmd()
        enqa.opcode = zhpe.XDM_CMD.ENQA
        enqa.enqa.dgcid = zuu.gcid
        enqa.enqa.rspctxid = self.rdm.rsp_rqa.info.rspctxid
        enqa.enqa.payload[0:len4] = str4
        if use_buffer == True:
            xdm.buffer_cmd(enqa)
        else:
            xdm.queue_cmd(enqa)
        try:
            enqa_cmpl = self.xdm.get_cmpl()
            if args.verbosity:
                print('ENQA cmpl: {}'.format(enqa_cmpl))
        except XDMcompletionError as e:
            print('ENQA cmpl error: {} {:#x} request_id {:#x}'.format(
                e, e.status, e.request_id))
        if use_poll == True:
            rdm_cmpls = self.rdm.get_poll()
        else:
            rdm_cmpls = self.rdm.get_cmpl()
        if args.verbosity:
            for c in range(len(rdm_cmpls)):
                if c != None:
                    print('RDM cmpl: {}'.format(rdm_cmpls[c].enqa))
        for c in range(len(rdm_cmpls)):
            if enqa.enqa.payload[0:52] != rdm_cmpls[c].enqa.payload[0:52]:
                print('FAIL: RDM: payload is {} and should be {}'.format(
                    rdm_cmpls[c].enqa.payload[0:52], enqa.enqa.payload[0:52]))

    def FAM_tests(self, gcid=0x40, size=2 << 20):
        # default CID for Carbon is 0x40 and create a ZUUID
        fam_zuu = zuuid(gcid)
        if args.verbosity:
            print('FAM zuuid={}'.format(fam_zuu))
        # Do a UUID_IMPORT with the ZHPE_IS_FAM flag set
        conn.do_UUID_IMPORT(fam_zuu, 1, None)
        # RMR_IMPORT the FAM at address 0 and size 2M
        sz2M = 2 << 20
        FAMaccess = (zhpe.MR.GET_REMOTE | zhpe.MR.PUT_REMOTE
                     | zhpe.MR.INDIVIDUAL | zhpe.MR.REQ_CPU)
        self.fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, size, FAMaccess)
        # Do load/store to FAM at address 0
        self.fam_rmm = mmap.mmap(f.fileno(), size, offset=fam_rmr.offset)
        self.fam_v, self.fam_l = zhpe.mmap_vaddr_len(self.fam_rmm)
        for off in range(0, 64, 7):
            self.test.load_store(offset=off, use_fam=True)

        if args.fam:
            fam_zuu = zuuid(gcid=args.fam_gcid)
            print('FAM zuuid={}'.format(fam_zuu))
            # Do a UUID_IMPORT with the ZHPE_IS_FAM flag set
            conn.do_UUID_IMPORT(fam_zuu, UU.IS_FAM, None)
            # RMR_IMPORT the FAM at address 0 and size 2M
            if args.load_store:
                fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, sz2M, MR.GRPRIC)
                # Do load/store to FAM
                fam_rmm = mmap.mmap(f.fileno(), sz2M, offset=fam_rmr.offset)
                fam_v, fam_l = zhpe.mmap_vaddr_len(fam_rmm)
                fam_rmm[0:len1] = str1
                fam_rmm[len1:len1_2] = str2
                # flush writes, so reads will see new data
                zhpe.pmem_flush(fam_v, len1_2)
            else:
                fam_rmr = conn.do_RMR_IMPORT(fam_zuu, 0, sz2M, MR.GRPRI)
            # do an XDM command to get the data back and check it
            get_imm = zhpe.xdm_cmd()
            get_imm.opcode = zhpe.XDM_CMD.GET_IMM
            get_imm.getput_imm.size = len1_2
            get_imm.getput_imm.rem_addr = fam_rmr.req_addr
            xdm.queue_cmd(get_imm)
            try:
                get_imm_cmpl = xdm.get_cmpl()
                if args.verbosity:
                    print('GET_IMM cmpl: {}'.format(get_imm_cmpl.getimm))
                # Verify the payload is what we expect
                if args.load_store:
                    retstr = bytearray(get_imm_cmpl.getimm.payload[0:len1_2])
                    if retstr == str1 + str2:
                        if args.verbosity:
                            print('FAM comparision PASS')
                    else:
                        print('FAM comparision FAIL')
            except XDMcompletionError as e:
                print('GET_IMM cmpl error: {} {:#x} request_id {:#x}'.format(
                    e, e.status, e.request_id))

    def all_tests(self):
        for off in range(0, 64, 7):
            self.test_load_store(offset=off)
        self.test_PUT_IMM()
        self.test_GET_IMM()
        self.test_PUT()
        self.test_GET()
        self.test_ATOMIC_SWAP32(data=0x12345678)
        self.test_ATOMIC_CAS32(data1=0x12345678, data2=0xBDA11ABC)
        self.test_ATOMIC_ADD32(data=0x137ff731)
        self.test_ATOMIC_SWAP64(data=0x1234567887654321)
        self.test_ATOMIC_CAS64(data1=0x1234567887654321,
                               data2=0xBDA11ABCBDA11ABC)
        self.test_ATOMIC_ADD64(data=0x137ff731137ff731)
        # Test XDM Command Buffers
        self.test_PUT_IMM(use_buffer=True)
        self.test_GET_IMM(use_buffer=True)
        self.test_PUT(use_buffer=True)
        self.test_GET(use_buffer=True)
        self.test_ATOMIC_SWAP32(use_buffer=True, data=0x12345678)
        self.test_ATOMIC_CAS32(use_buffer=True,
                               data1=0x12345678,
                               data2=0xBDA11ABC)
        self.test_ATOMIC_ADD32(use_buffer=True, data=0x137ff731)
        self.test_ATOMIC_SWAP64(use_buffer=True, data=0x1234567887654321)
        self.test_ATOMIC_CAS64(use_buffer=True,
                               data1=0x1234567887654321,
                               data2=0xBDA11ABCBDA11ABC)
        self.test_ATOMIC_ADD64(use_buffer=True, data=0x137ff731137ff731)