From 2c2ab34ee31409928bfdca77c1d7d9114765cf43 Mon Sep 17 00:00:00 2001 From: ericxiett Date: Wed, 11 Aug 2021 16:55:56 +0800 Subject: [PATCH] Add xilinx fpga driver This patch implemented Xilinx FPGA driver in cyborg. Currently can bind xilinx card to guest, and support programming. Implements: blueprint add-xilinx-fpga-driver Change-Id: I8cf48f64a0754ff13da2dddc4fabc601087b14b1 --- .../drivers/fpga/xilinx/__init__.py | 0 .../accelerator/drivers/fpga/xilinx/driver.py | 74 +++++++ .../drivers/fpga/xilinx/sysinfo.py | 188 ++++++++++++++++++ cyborg/api/controllers/v2/arqs.py | 15 +- .../accelerator/drivers/fpga/test_base.py | 4 +- .../drivers/fpga/xilinx/__init__.py | 0 .../drivers/fpga/xilinx/test_driver.py | 129 ++++++++++++ .../unit/api/controllers/v2/test_arqs.py | 20 ++ cyborg/tests/unit/fake_device_profile.py | 20 ++ cyborg/tests/unit/fake_extarq.py | 44 ++++ ...d-xilinx-fpga-driver-1c98c6a95d8e1f9b.yaml | 6 + setup.cfg | 1 + 12 files changed, 497 insertions(+), 4 deletions(-) create mode 100644 cyborg/accelerator/drivers/fpga/xilinx/__init__.py create mode 100644 cyborg/accelerator/drivers/fpga/xilinx/driver.py create mode 100644 cyborg/accelerator/drivers/fpga/xilinx/sysinfo.py create mode 100644 cyborg/tests/unit/accelerator/drivers/fpga/xilinx/__init__.py create mode 100644 cyborg/tests/unit/accelerator/drivers/fpga/xilinx/test_driver.py create mode 100644 releasenotes/notes/add-xilinx-fpga-driver-1c98c6a95d8e1f9b.yaml diff --git a/cyborg/accelerator/drivers/fpga/xilinx/__init__.py b/cyborg/accelerator/drivers/fpga/xilinx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cyborg/accelerator/drivers/fpga/xilinx/driver.py b/cyborg/accelerator/drivers/fpga/xilinx/driver.py new file mode 100644 index 00000000..0dc1265b --- /dev/null +++ b/cyborg/accelerator/drivers/fpga/xilinx/driver.py @@ -0,0 +1,74 @@ +# Copyright 2021 Inspur, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +""" +Cyborg Xilinx FPGA driver implementation. +""" +from oslo_concurrency import processutils + +from cyborg.accelerator.drivers.fpga.base import FPGADriver +from cyborg.accelerator.drivers.fpga.xilinx import sysinfo +from cyborg.common import exception +import cyborg.privsep + + +@cyborg.privsep.sys_admin_pctxt.entrypoint +def _fpga_program_privileged(cmd_args): + # Example: xbmgmt program --device 0000:d8:00.0 + # --base --image xilinx-u250-gen3x16-base + cmd = ["/opt/xilinx/xrt/bin/xbmgmt"] + cmd.extend(cmd_args) + return processutils.execute(*cmd) + + +class XilinxFPGADriver(FPGADriver): + """Class for Xilinx FPGA drivers. + Vendor should implement their specific drivers in this class. + """ + VENDOR = "xilinx" + + def __init__(self, *args, **kwargs): + pass + + def discover(self): + return sysinfo.fpga_tree() + + def program(self, controlpath_id, image_file_path): + """Program the FPGA with the provided bitstream image. + + :param controlpath_id: the ID of controlpath. + :param image_file_path: the path of the image file + :returns: True on success, False on failure + """ + if controlpath_id['cpid_type'] != "PCI": + raise exception.InvalidType(obj='controlpath_id', + type=controlpath_id['cpid_type'], + expected='PCI') + cmd_args = ['program'] + cmd_args.append('--device') + bdf_dict = controlpath_id['cpid_info'] + # BDF format: domain:bus:device:function + bdf = ':'.join([s for s in map(lambda x: bdf_dict[x], + ['domain', 'bus', 'device', 'function'])]) + cmd_args.append(bdf) + cmd_args.append('--base') + cmd_args.append('--image') + cmd_args.append(image_file_path) + + try: + _fpga_program_privileged(cmd_args) + return True + except Exception: + return False diff --git a/cyborg/accelerator/drivers/fpga/xilinx/sysinfo.py b/cyborg/accelerator/drivers/fpga/xilinx/sysinfo.py new file mode 100644 index 00000000..d0decc8e --- /dev/null +++ b/cyborg/accelerator/drivers/fpga/xilinx/sysinfo.py @@ -0,0 +1,188 @@ +# Copyright 2021 Inspur, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Cyborg Xilinx FPGA driver implementation. +""" + +import re + +from oslo_concurrency import processutils +from oslo_log import log as logging +from oslo_serialization import jsonutils + +from cyborg.accelerator.common import utils +from cyborg.common import constants +from cyborg.conf import CONF +from cyborg.objects.driver_objects import driver_attach_handle +from cyborg.objects.driver_objects import driver_attribute +from cyborg.objects.driver_objects import driver_controlpath_id +from cyborg.objects.driver_objects import driver_deployable +from cyborg.objects.driver_objects import driver_device +from cyborg.privsep import sys_admin_pctxt + +LOG = logging.getLogger(__name__) + +XILINX_FPGA_FLAGS = ["Xilinx Corporation Device", + "Processing accelerators"] + +XILINX_FPGA_INFO_PATTERN = re.compile( + r"(?P[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:" + r"[0-9a-fA-F]{2}\.[0-9a-fA-F]) " + r"(?P.*) [\[].*]: (?P.*) .*" + r"[\[](?P[0-9a-fA-F]" + r"{4}):(?P[0-9a-fA-F]{4})].*") + +XILINX_PF_MAPS = {"mgmt": "xclmgmt", "user": "xocl"} + +VENDOR_MAPS = {"10ee": "xilinx"} + + +@sys_admin_pctxt.entrypoint +def lspci_privileged(cmd): + return processutils.execute(*cmd) + + +def get_pci_devices(pci_flags, vendor_id=None): + device_for_vendor_out = [] + all_device_out = [] + cmd = ['lspci', '-nnn', '-D'] + lspci_out = lspci_privileged(cmd)[0].split('\n') + for i in range(len(lspci_out)): + if all(x in lspci_out[i] for x in pci_flags): + all_device_out.append(lspci_out[i]) + if vendor_id and vendor_id in lspci_out[i]: + device_for_vendor_out.append(lspci_out[i]) + return device_for_vendor_out if vendor_id else all_device_out + + +def _generate_traits(vendor_id, product_id): + """Generate traits for FPGAs. + : param vendor_id: vendor_id of FPGA + : param product_id: product_id of FPGA + Example FPGA traits: + {traits:["CUSTOM_FPGA_XILINX", "CUSTOM_FPGA_PRODUCT_ID_5001"]} + """ + traits = [] + traits.append("CUSTOM_FPGA_" + VENDOR_MAPS.get(vendor_id, "").upper()) + traits.append("CUSTOM_FPGA_PRODUCT_ID_" + product_id.upper()) + return {"traits": traits} + + +def _get_pf_type(device): + cmd = ['lspci', '-k', '-s', device] + result = lspci_privileged(cmd)[0] + for k, v in XILINX_PF_MAPS.items(): + if v in result: + return k + + +def _combine_device_by_pci_func(pci_devices): + fpga_devices = [] + for pci_dev in pci_devices: + m = XILINX_FPGA_INFO_PATTERN.match(pci_dev) + if m: + pci_dict = m.groupdict() + LOG.debug('Xilinx fpga pci device in dict: %s', pci_dict) + # 0000:3b:00.0/1 + is_existed = False + new_addr = pci_dict.get('pci_addr') + for fpga in fpga_devices: + existed_addr = fpga.get('pci_addr')[0] + # compare domain:bus:slot + if existed_addr and \ + new_addr.split('.')[0] == existed_addr.split('.')[0]: + fpga.update({'pci_addr': [existed_addr, new_addr]}) + is_existed = True + if not is_existed: + traits = _generate_traits(pci_dict["vendor_id"], + pci_dict["product_id"]) + pci_dict["rc"] = constants.RESOURCES["FPGA"] + pci_dict.update(traits) + pci_dict.update({'pci_addr': [new_addr]}) + fpga_devices.append(pci_dict) + return fpga_devices + + +def _generate_controlpath_id(fpga): + driver_cpid = driver_controlpath_id.DriverControlPathID() + driver_cpid.cpid_type = "PCI" + driver_cpid.cpid_info = utils.pci_str_to_json(fpga['pci_addr'][0]) + return driver_cpid + + +def _generate_dep_list(fpga): + dep_list = [] + driver_dep = driver_deployable.DriverDeployable() + driver_dep.attribute_list = _generate_attribute_list(fpga) + driver_dep.attach_handle_list = [] + driver_dep.name = CONF.host + '_' + fpga["pci_addr"][0] + driver_dep.driver_name = VENDOR_MAPS.get(fpga["vendor_id"]).upper() + driver_dep.num_accelerators = 1 + driver_dep.attach_handle_list = _generate_attach_handle(fpga) + dep_list.append(driver_dep) + return dep_list + + +def _generate_attribute_list(fpga): + attr_list = [] + for k, v in fpga.items(): + if k == "rc": + driver_attr = driver_attribute.DriverAttribute() + driver_attr.key, driver_attr.value = k, v + attr_list.append(driver_attr) + if k == "traits": + values = fpga.get(k, []) + for index, val in enumerate(values): + driver_attr = driver_attribute.DriverAttribute( + key="trait" + str(index), value=val) + attr_list.append(driver_attr) + return attr_list + + +def _generate_attach_handle(fpga): + driver_ahs = [] + for addr in fpga.get('pci_addr'): + driver_ah = driver_attach_handle.DriverAttachHandle() + driver_ah.attach_type = constants.AH_TYPE_PCI + driver_ah.attach_info = utils.pci_str_to_json(addr) + driver_ah.in_use = False + driver_ahs.append(driver_ah) + return driver_ahs + + +def fpga_tree(): + fpga_list = [] + fpga_pci_devices = get_pci_devices(XILINX_FPGA_FLAGS) + LOG.debug("Xilinx fpga devices from lspci: %s", fpga_pci_devices) + # In the return pci devices, mgmt pf and user pf are two entries. + # Now only when binding both to vm, end user can program it. + # So combine these two entries into one device. + for fpga in _combine_device_by_pci_func(fpga_pci_devices): + driver_device_obj = driver_device.DriverDevice() + driver_device_obj.vendor = fpga["vendor_id"] + driver_device_obj.model = fpga.get('model', 'miss model info') + std_board_info = {'product_id': fpga.get('product_id', None), + 'controller': fpga.get('controller', None)} + vendor_board_info = { + 'vendor_info': fpga.get('vendor_info', 'fpga_vb_info')} + driver_device_obj.std_board_info = jsonutils.dumps(std_board_info) + driver_device_obj.vendor_board_info = \ + jsonutils.dumps(vendor_board_info) + driver_device_obj.type = constants.DEVICE_FPGA + driver_device_obj.stub = fpga.get('stub', False) + driver_device_obj.controlpath_id = _generate_controlpath_id(fpga) + driver_device_obj.deployable_list = _generate_dep_list(fpga) + fpga_list.append(driver_device_obj) + return fpga_list diff --git a/cyborg/api/controllers/v2/arqs.py b/cyborg/api/controllers/v2/arqs.py index aeb0d610..f4019bd3 100644 --- a/cyborg/api/controllers/v2/arqs.py +++ b/cyborg/api/controllers/v2/arqs.py @@ -135,9 +135,18 @@ class ARQsController(base.CyborgController): extarq_list = [] for group_id, group in enumerate(devprof.groups): - accel_resources = [ - int(val) for key, val in group.items() - if key.startswith('resources')] + accel_resources = [] + # If the device profile requires the Xilinx fpga, the number of + # resources should multiply by 2 cause that end user can program + # the device only when both MGMT and USER PF are bound to + # instance. + if group.get("trait:CUSTOM_FPGA_XILINX") == "required": + accel_resources = [int(group.get("resources:FPGA"))] * 2 + else: + accel_resources = [ + int(val) for key, val in group.items() + if key.startswith('resources')] + # If/when we introduce non-accelerator resources, like # device-local memory, the key search above needs to be # made specific to accelerator resources only. diff --git a/cyborg/tests/unit/accelerator/drivers/fpga/test_base.py b/cyborg/tests/unit/accelerator/drivers/fpga/test_base.py index 76868fd9..89c18b08 100644 --- a/cyborg/tests/unit/accelerator/drivers/fpga/test_base.py +++ b/cyborg/tests/unit/accelerator/drivers/fpga/test_base.py @@ -15,6 +15,7 @@ from cyborg.accelerator.drivers.fpga.base import FPGADriver from cyborg.accelerator.drivers.fpga.intel.driver import IntelFPGADriver # noqa from cyborg.accelerator.drivers.fpga.inspur.driver import InspurFPGADriver # noqa +from cyborg.accelerator.drivers.fpga.xilinx.driver import XilinxFPGADriver # noqa from cyborg.tests import base @@ -22,7 +23,8 @@ class TestFPGADriver(base.TestCase): def test_create(self): FPGADriver.create("intel") FPGADriver.create("inspur") - self.assertRaises(LookupError, FPGADriver.create, "xilinx") + FPGADriver.create("xilinx") + self.assertRaises(LookupError, FPGADriver.create, "fake") def test_discover(self): d = FPGADriver() diff --git a/cyborg/tests/unit/accelerator/drivers/fpga/xilinx/__init__.py b/cyborg/tests/unit/accelerator/drivers/fpga/xilinx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cyborg/tests/unit/accelerator/drivers/fpga/xilinx/test_driver.py b/cyborg/tests/unit/accelerator/drivers/fpga/xilinx/test_driver.py new file mode 100644 index 00000000..ef1199cd --- /dev/null +++ b/cyborg/tests/unit/accelerator/drivers/fpga/xilinx/test_driver.py @@ -0,0 +1,129 @@ +# Copyright 2021 Inspur, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_serialization import jsonutils +from unittest import mock + +from cyborg.accelerator.drivers.fpga.xilinx.driver import XilinxFPGADriver +from cyborg.tests import base + +XILINX_FPGA_INFO = ["0000:3b:00.0 Processing accelerators [1200]: " + "Xilinx Corporation Device [10ee:5000]\n" + "0000:3b:00.1 Processing accelerators [1200]: " + "Xilinx Corporation Device [10ee:5001]"] + + +def fake_output(arg): + if arg == ['lspci', '-nnn', '-D']: + return XILINX_FPGA_INFO + + +class TestXilinxFPGADriver(base.TestCase): + + def setUp(self): + super(TestXilinxFPGADriver, self).setUp() + + @mock.patch('cyborg.accelerator.drivers.fpga.' + 'xilinx.sysinfo.lspci_privileged') + def test_discover(self, mock_devices_for_vendor): + mock_devices_for_vendor.side_effect = fake_output + self.set_defaults(host='fake-host', debug=True) + fpga_list = XilinxFPGADriver().discover() + self.assertEqual(1, len(fpga_list)) + attach_handle_list = [ + {'attach_type': 'PCI', + 'attach_info': '{"bus": "3b", ' + '"device": "00", ' + '"domain": "0000", ' + '"function": "0"}', + 'in_use': False}, + {'attach_type': 'PCI', + 'attach_info': '{"bus": "3b", ' + '"device": "00", ' + '"domain": "0000", ' + '"function": "1"}', + 'in_use': False} + ] + attribute_list = [ + {'key': 'rc', 'value': 'FPGA'}, + {'key': 'trait0', 'value': 'CUSTOM_FPGA_XILINX'}, + {'key': 'trait1', 'value': 'CUSTOM_FPGA_PRODUCT_ID_5000'}, + ] + expected = { + 'vendor': '10ee', + 'type': 'FPGA', + 'std_board_info': {"controller": "Processing accelerators", + "product_id": "5000"}, + 'vendor_board_info': {"vendor_info": "fpga_vb_info"}, + 'deployable_list': + [ + { + 'num_accelerators': 1, + 'driver_name': 'XILINX', + 'name': 'fake-host_0000:3b:00.0', + 'attach_handle_list': attach_handle_list, + 'attribute_list': attribute_list + }, + ], + 'controlpath_id': {'cpid_info': '{"bus": "3b", ' + '"device": "00", ' + '"domain": "0000", ' + '"function": "0"}', + 'cpid_type': 'PCI'} + } + fpga_obj = fpga_list[0] + fpga_dict = fpga_obj.as_dict() + fpga_dep_list = fpga_dict['deployable_list'] + fpga_attach_handle_list = ( + fpga_dep_list[0].as_dict()['attach_handle_list']) + fpga_attribute_list = fpga_dep_list[0].as_dict()['attribute_list'] + attri_obj_data = [] + [attri_obj_data.append(attr.as_dict()) for attr in fpga_attribute_list] + attribute_actual_data = sorted(attri_obj_data, key=lambda i: i['key']) + self.assertEqual(expected['vendor'], fpga_dict['vendor']) + self.assertEqual(expected['controlpath_id'], + fpga_dict['controlpath_id']) + self.assertEqual(expected['std_board_info'], + jsonutils.loads(fpga_dict['std_board_info'])) + self.assertEqual(expected['vendor_board_info'], + jsonutils.loads(fpga_dict['vendor_board_info'])) + self.assertEqual(expected['deployable_list'][0]['num_accelerators'], + fpga_dep_list[0].as_dict()['num_accelerators']) + self.assertEqual(expected['deployable_list'][0]['name'], + fpga_dep_list[0].as_dict()['name']) + self.assertEqual(expected['deployable_list'][0]['driver_name'], + fpga_dep_list[0].as_dict()['driver_name']) + self.assertEqual(2, len(fpga_attach_handle_list)) + self.assertEqual(attach_handle_list[0], + fpga_attach_handle_list[0].as_dict()) + self.assertEqual(attach_handle_list[1], + fpga_attach_handle_list[1].as_dict()) + self.assertEqual(attribute_list, attribute_actual_data) + + @mock.patch('cyborg.accelerator.drivers.fpga.xilinx.driver.' + '_fpga_program_privileged') + def test_program(self, mock_prog): + bdf = '0000:3b:00:0' + expect_cmd_args = ['program', '--device', bdf, '--base', + '--image', '/path/image'] + + xilinx_driver = XilinxFPGADriver() + cpid_info = {"domain": "0000", "bus": "3b", + "device": "00", "function": "0"} + cpid = {'cpid_type': 'PCI', 'cpid_info': cpid_info} + + # program PF + mock_prog.return_value = bytes([0]) + xilinx_driver.program(cpid, "/path/image") + mock_prog.assert_called_with(expect_cmd_args) diff --git a/cyborg/tests/unit/api/controllers/v2/test_arqs.py b/cyborg/tests/unit/api/controllers/v2/test_arqs.py index ae0fd595..8fd9501d 100644 --- a/cyborg/tests/unit/api/controllers/v2/test_arqs.py +++ b/cyborg/tests/unit/api/controllers/v2/test_arqs.py @@ -203,6 +203,26 @@ class TestARQsController(v2_test.APITestV2): dp_group_id = idx self.assertEqual(dp_group_id, out_arq['device_profile_group_id']) + @mock.patch('cyborg.objects.DeviceProfile.get_by_name') + @mock.patch('cyborg.conductor.rpcapi.ConductorAPI.arq_create') + def test_create_with_xilinx_fpga(self, mock_obj_extarq, mock_obj_dp): + xilinx_fpga_dp = fake_device_profile.get_xilinx_fpga_devprof() + mock_obj_dp.return_value = dp = xilinx_fpga_dp + fake_xilinx_fpga_objs = fake_extarq.get_fake_xilinx_fpga_extarq_objs() + mock_obj_extarq.side_effect = fake_xilinx_fpga_objs + params = {'device_profile_name': dp['name']} + response = self.post_json(self.ARQ_URL, params, headers=self.headers) + data = jsonutils.loads(response.__dict__['controller_output']) + out_arqs = data['arqs'] + + self.assertEqual(HTTPStatus.CREATED, response.status_int) + self.assertEqual(len(out_arqs), 2) + for in_extarq, out_arq in zip(fake_xilinx_fpga_objs, out_arqs): + self._validate_arq(in_extarq.arq, out_arq) + for idx, out_arq in enumerate(out_arqs): + dp_group_id = idx + self.assertEqual(dp_group_id, out_arq['device_profile_group_id']) + @mock.patch('cyborg.objects.DeviceProfile.get_by_name') @mock.patch('cyborg.objects.ExtARQ.create') def test_create_with_wrong_dp(self, mock_obj_extarq, mock_obj_dp): diff --git a/cyborg/tests/unit/fake_device_profile.py b/cyborg/tests/unit/fake_device_profile.py index 469b88a8..d515b983 100644 --- a/cyborg/tests/unit/fake_device_profile.py +++ b/cyborg/tests/unit/fake_device_profile.py @@ -106,3 +106,23 @@ def get_db_devprofs(): dp_list = _get_device_profiles_as_dict() db_devprofs = list(map(_convert_to_db_devprof, dp_list)) return db_devprofs + + +def get_xilinx_fpga_devprof(): + xilinx_fpga_dp = { + "id": 3, + "uuid": "638828d7-7145-4060-904c-894aa726b133", + "name": 'fake_xilinx_fpga_dp', + "description": "fake_xilinx_fpga_dp-desc", + "created_at": datetime.datetime( + 2022, 1, 22, 10, 40, 56, + tzinfo=datetime.timezone.utc), + "updated_at": None, + "groups": [ + {"resources:FPGA": "1", + "trait:CUSTOM_FPGA_XILINX": "required", + "trait:CUSTOM_FPGA_PRODUCT_ID_5000": "required", + }, + ] + } + return _convert_to_obj(xilinx_fpga_dp) diff --git a/cyborg/tests/unit/fake_extarq.py b/cyborg/tests/unit/fake_extarq.py index 5556527b..d191b37d 100644 --- a/cyborg/tests/unit/fake_extarq.py +++ b/cyborg/tests/unit/fake_extarq.py @@ -300,3 +300,47 @@ def get_patch_list(same_device=True): 'value': dev_uuid} patch_list[newarq['uuid']] = [host_binding, inst_binding, dev_binding] return patch_list, device_rp_uuid + + +def get_fake_xilinx_fpga_extarq_objs(): + arqs = [ + {"uuid": 'b8c19eb2-e03c-47b4-b7cf-ced6086b2d11', + "device_profile_group_id": 0, + "state": "Initial", + "device_profile_name": "fake_xilinx_fpga_dp", + "hostname": "myhost", + "instance_uuid": "5922a70f-1e06-4cfd-88dd-a332120d7144", + "attach_handle_type": "PCI", + # attach_handle info should vary across ARQs but ignored for testing + "attach_handle_info": { + "bus": "3b", + "device": "00", + "domain": "0000", + "function": "0" + }, + "device_profile_group": { + "trait:CUSTOM_FPGA_XILINX": "required", + "resources:FPGA": "1", + "trait:CUSTOM_FPGA_PRODUCT_ID_5000": "required"} + }, + {"uuid": '012955c7-90f9-45a9-bb7d-7c2907d8997f', + "device_profile_group_id": 1, + "state": "Initial", + "device_profile_name": "fake_xilinx_fpga_dp", + "hostname": "myhost", + "instance_uuid": "5922a70f-1e06-4cfd-88dd-a332120d7144", + "attach_handle_type": "PCI", + # attach_handle info should vary across ARQs but ignored for testing + "attach_handle_info": { + "bus": "3b", + "device": "00", + "domain": "0000", + "function": "1" + }, + "device_profile_group": { + "trait:CUSTOM_FPGA_XILINX": "required", + "resources:FPGA": "1", + "trait:CUSTOM_FPGA_PRODUCT_ID_5000": "required"} + }, + ] + return list(map(_convert_from_dict_to_obj, arqs)) diff --git a/releasenotes/notes/add-xilinx-fpga-driver-1c98c6a95d8e1f9b.yaml b/releasenotes/notes/add-xilinx-fpga-driver-1c98c6a95d8e1f9b.yaml new file mode 100644 index 00000000..70230c36 --- /dev/null +++ b/releasenotes/notes/add-xilinx-fpga-driver-1c98c6a95d8e1f9b.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add the Xilinx FPGA driver. Through this driver Cyborg can manage Xilinx + FPGA devices, including discovering devices' info and programming + ``xclbin``. diff --git a/setup.cfg b/setup.cfg index e4c83d89..3e19b400 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,7 @@ cyborg.database.migration_backend = cyborg.accelerator.driver = intel_fpga_driver = cyborg.accelerator.drivers.fpga.intel.driver:IntelFPGADriver inspur_fpga_driver = cyborg.accelerator.drivers.fpga.inspur.driver:InspurFPGADriver + xilinx_fpga_driver = cyborg.accelerator.drivers.fpga.xilinx.driver:XilinxFPGADriver nvmf_spdk_driver = cyborg.accelerator.drivers.spdk.nvmf.nvmf:NVMFDRIVER nvidia_gpu_driver = cyborg.accelerator.drivers.gpu.nvidia.driver:NVIDIAGPUDriver fake_driver = cyborg.accelerator.drivers.fake:FakeDriver