Skip to content

Commit

Permalink
Refactor codes to support different device drivers (#106)
Browse files Browse the repository at this point in the history
* Add test files for rngd (devfs only)
* Adopt ArchFamily, separate list & get api between archs
* Remove sysfs enums
* Add some test sysfs files for renegade except
files excluded:
- perf_regs (at /sys/class/renegade_mgmt/<pe dir>)
- numa_node (at /sys/bus/pci/devices/<bus>)

* Add impls for Warboy, Renegade
* Integrate changes into python binding
* Add test code for device impls and fix failing tests
* Remove ArchFamily and move arch-specific methods into Arch
* Inject target directory from outside of list_devfs
* Prevent panic inside numa_node()
* Remove unnecessary comment in bindings/python/src/arch.rs
* Make Arch's methods to use &self instead
* Make read_mgmt_to_string to take AsRef<Path> instead of PathBuf
* Merge arch and arch_impl into one
* Reduce repeated codes between Archs
* Rename device_index to devfile_index, and deprecate get_device()
* Change the format of `name()` API and remove remaining `device_index`
* Remove `arch` from each Arch implementations
* Fix broken python interface and tests
  • Loading branch information
n0gu-furiosa authored Apr 23, 2024
1 parent 2be72d3 commit eebaf7e
Show file tree
Hide file tree
Showing 137 changed files with 1,076 additions and 695 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions bindings/python/furiosa_device.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ from enum import Enum
from typing import Dict, List, Tuple, Union

class Arch(Enum):
Warboy = ...
WarboyB0 = ...
Renegade = ...
U250 = ...

class DeviceMode(Enum):
Single = ...
Expand Down Expand Up @@ -47,7 +45,7 @@ class ClockFrequency:
class DeviceConfig:
def __new__(
cls,
_arch: Arch = Arch.Warboy,
_arch: Arch = Arch.WarboyB0,
mode: DeviceMode = DeviceMode.Fusion,
count: int = 1,
) -> DeviceConfig: ...
Expand All @@ -60,7 +58,7 @@ class DeviceConfig:
class DeviceFile:
def path(self) -> str: ...
def filename(self) -> str: ...
def device_index(self) -> int: ...
def devfile_index(self) -> int: ...
def core_range(self) -> CoreRange: ...
def mode(self) -> DeviceMode: ...
def __repr(self) -> str: ...
Expand All @@ -87,7 +85,7 @@ class PerformanceCounter:

class Device:
def name(self) -> str: ...
def device_index(self) -> int: ...
def devfile_index(self) -> int: ...
def arch(self) -> Arch: ...
def alive(self) -> bool: ...
def atr_error(self) -> List[Dict[str, int]]: ...
Expand Down
12 changes: 9 additions & 3 deletions bindings/python/src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ use pyo3::prelude::*;
#[pyclass(name = "Arch")]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ArchPy {
WarboyA0,
// It indicates WarboyB0 since WarboyB0 is the default
Warboy,
Renegade,
U250,
}

impl From<ArchPy> for furiosa_device::Arch {
fn from(arch_family: ArchPy) -> Self {
match arch_family {
ArchPy::Warboy => furiosa_device::Arch::WarboyB0,
ArchPy::Renegade => furiosa_device::Arch::Renegade,
}
}
}
1 change: 0 additions & 1 deletion bindings/python/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ impl DeviceConfigPy {
#[pyo3(signature = (arch=ArchPy::Warboy, mode=DeviceModePy::Fusion, count=1))]
fn py_new(arch: ArchPy, mode: DeviceModePy, count: u8) -> PyResult<DeviceConfigPy> {
let config = match arch {
ArchPy::WarboyA0 => DeviceConfig::warboy_a0(),
ArchPy::Warboy => DeviceConfig::warboy(),
_ => {
return Err(PyRuntimeError::new_err(format!(
Expand Down
36 changes: 17 additions & 19 deletions bindings/python/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,16 @@ impl DevicePy {
self.inner.name()
}

/// Returns the device index (e.g., 0 for npu0).
fn device_index(&self) -> u8 {
self.inner.device_index()
/// Returns the device file index (e.g., 0 for npu0).
fn devfile_index(&self) -> u8 {
self.inner.devfile_index()
}

/// Returns `Arch` of the device(e.g., `Warboy`).
fn arch(&self) -> ArchPy {
match self.inner.arch() {
Arch::WarboyA0 => ArchPy::WarboyA0,
Arch::WarboyB0 => ArchPy::Warboy,
Arch::Renegade => ArchPy::Renegade,
Arch::U250 => ArchPy::U250,
}
}

Expand All @@ -158,33 +156,33 @@ impl DevicePy {
}

/// Returns PCI bus number of the device.
fn busname(&self) -> PyResult<String> {
self.inner.busname().map_err(to_py_err)
fn busname(&self) -> String {
self.inner.busname()
}

/// Returns PCI device ID of the device.
fn pci_dev(&self) -> PyResult<String> {
self.inner.pci_dev().map_err(to_py_err)
fn pci_dev(&self) -> String {
self.inner.pci_dev()
}

/// Returns serial number of the device.
fn device_sn(&self) -> PyResult<String> {
self.inner.device_sn().map_err(to_py_err)
fn device_sn(&self) -> String {
self.inner.device_sn()
}

/// Returns UUID of the device.
fn device_uuid(&self) -> PyResult<String> {
self.inner.device_uuid().map_err(to_py_err)
fn device_uuid(&self) -> String {
self.inner.device_uuid()
}

/// Retrieves firmware revision from the device.
fn firmware_version(&self) -> PyResult<String> {
self.inner.firmware_version().map_err(to_py_err)
fn firmware_version(&self) -> String {
self.inner.firmware_version()
}

/// Retrieves driver version for the device.
fn driver_version(&self) -> PyResult<String> {
self.inner.driver_version().map_err(to_py_err)
fn driver_version(&self) -> String {
self.inner.driver_version()
}

/// Returns uptime of the device.
Expand Down Expand Up @@ -362,8 +360,8 @@ impl DeviceFilePy {
}

/// Returns the device index (e.g., 1 for npu1pe0).
fn device_index(&self) -> u8 {
self.inner.device_index()
fn devfile_index(&self) -> u8 {
self.inner.devfile_index()
}

/// Returns the range of cores this device file may occupy.
Expand Down
22 changes: 1 addition & 21 deletions bindings/python/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use furiosa_device::{find_device_files, get_device, get_device_file, list_devices};
use furiosa_device::{find_device_files, get_device_file, list_devices};
use hwmon::FetcherPy;
use pyo3::prelude::*;

Expand Down Expand Up @@ -46,25 +46,6 @@ fn list_devices_python(py: Python<'_>) -> PyResult<&PyAny> {
})
}

/// `get_device` returns a specific Furiosa NPU device in the system.
/// One can simply call as below:
/// ```python
/// import asyncio
/// from furiosa_device import get_device
///
/// async def main():
/// device = await furiosa_device.get_device(0)
/// asyncio.run(main())
/// ```
///
/// `Device` offers methods for further information of each device.
#[pyfunction(name = "get_device")]
fn get_device_python(py: Python<'_>, idx: u8) -> PyResult<&PyAny> {
pyo3_asyncio::tokio::future_into_py(py, async move {
get_device(idx).await.map(DevicePy::new).map_err(to_py_err)
})
}

/// If you have a desired configuration, call `find_device_files` with your device configuration
/// described by a `DeviceConfig`. `find_device_files` will return a list of
/// `DeviceFile`s if there are matched devices.
Expand Down Expand Up @@ -115,7 +96,6 @@ fn get_device_file_python(py: Python<'_>, device_name: String) -> PyResult<&PyAn
#[pyo3(name = "furiosa_native_device")]
fn furiosa_device_python(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(list_devices_python, m)?)?;
m.add_function(wrap_pyfunction!(get_device_python, m)?)?;
m.add_function(wrap_pyfunction!(find_device_files_python, m)?)?;
m.add_function(wrap_pyfunction!(get_device_file_python, m)?)?;
m.add_class::<DevicePy>()?;
Expand Down
9 changes: 1 addition & 8 deletions bindings/python/src/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use furiosa_device::blocking::{find_device_files, get_device, get_device_file, list_devices};
use furiosa_device::blocking::{find_device_files, get_device_file, list_devices};
use pyo3::prelude::*;
use tokio::runtime::Runtime;

Expand Down Expand Up @@ -128,12 +128,6 @@ fn list_devices_python_sync(py: Python<'_>) -> PyResult<Vec<Py<PyAny>>> {
Ok(device_syncs)
}

/// This is sync version of get_device
#[pyfunction(name = "get_device")]
fn get_device_python_sync(idx: u8) -> PyResult<DevicePy> {
get_device(idx).map(DevicePy::new).map_err(to_py_err)
}

/// This is sync version of find_device_files
#[pyfunction(name = "find_device_files")]
fn find_device_files_python_sync(config: DeviceConfigPy) -> PyResult<Vec<DeviceFilePy>> {
Expand All @@ -158,7 +152,6 @@ fn get_device_file_python_sync(device_name: String) -> PyResult<DeviceFilePy> {
#[pyo3(name = "furiosa_native_device_sync")]
pub fn furiosa_device_python_sync(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(list_devices_python_sync, m)?)?;
m.add_function(wrap_pyfunction!(get_device_python_sync, m)?)?;
m.add_function(wrap_pyfunction!(find_device_files_python_sync, m)?)?;
m.add_function(wrap_pyfunction!(get_device_file_python_sync, m)?)?;
m.add_class::<DeviceSyncPy>()?;
Expand Down
15 changes: 4 additions & 11 deletions bindings/python/tests/test_fn.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,24 @@
DeviceConfig,
DeviceMode,
find_device_files,
get_device,
get_device_file,
list_devices,
)

def get_first_device(pattern):
return sorted(glob.glob(pattern))[0]

def get_first_device_name(pattern):
return sorted(glob.glob(pattern))[0].split("/")[-1]
return get_first_device(pattern).split("/")[-1]


@pytest.mark.asyncio
async def test_list_devices():
dev_name = get_first_device_name("/dev/npu*")
dev_name = get_first_device("/dev/npu*")
devices = await list_devices()
assert devices[0].name() == dev_name


@pytest.mark.asyncio
async def test_get_device():
dev_name = get_first_device_name("/dev/npu*")
dev_idx = int(dev_name.replace("npu", ""))
device = await get_device(dev_idx)
assert device.name() == dev_name


@pytest.mark.asyncio
async def test_find_device_files():
dev_name = get_first_device_name("/dev/npu*pe0-1")
Expand Down
15 changes: 4 additions & 11 deletions bindings/python/tests/test_fn_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,22 @@
from furiosa_native_device import Arch, DeviceConfig, DeviceMode
from furiosa_native_device.sync import (
find_device_files,
get_device,
get_device_file,
list_devices,
)

def get_first_device(pattern):
return sorted(glob.glob(pattern))[0]

def get_first_device_name(pattern):
return sorted(glob.glob(pattern))[0].split("/")[-1]

return get_first_device(pattern).split("/")[-1]

def test_list_devices():
dev_name = get_first_device_name("/dev/npu*")
dev_name = get_first_device("/dev/npu*")
devices = list_devices()
assert devices[0].name() == dev_name


def test_get_device():
dev_name = get_first_device_name("/dev/npu*")
dev_idx = int(dev_name.replace("npu", ""))
device = get_device(dev_idx)
assert device.name() == dev_name


def test_find_device_files():
dev_name = get_first_device_name("/dev/npu*pe0-1")
config = DeviceConfig(arch=Arch.Warboy, mode=DeviceMode.Fusion, count=1)
Expand Down
1 change: 1 addition & 0 deletions device-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ path = "bin/list_clock_frequency.rs"
[dependencies]
array_tool = "1"
cli-table = "0.4"
dyn-clone = "1.0.17"
enum-display-derive = "0.1"
enum-utils = "0.1.2"
itertools = "0.10"
Expand Down
2 changes: 1 addition & 1 deletion device-api/bin/list_clock_frequency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use furiosa_device::{list_devices, DeviceError};
#[tokio::main]
async fn main() -> Result<(), DeviceError> {
for device in list_devices().await? {
println!("-- npu{} --", device.device_index());
println!("-- {} --", device);
for frequency in device.clock_frequency()? {
println!(
"{:15}: {} {}",
Expand Down
2 changes: 1 addition & 1 deletion device-api/bin/list_hwmon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ async fn main() -> Result<(), DeviceError> {
for device in list_devices().await? {
let fetcher = device.get_hwmon_fetcher();

println!("-- npu{} --", device.device_index());
println!("-- {} --", device);
println!("Current");
for sensor_value in fetcher.read_currents().await? {
println!(
Expand Down
2 changes: 1 addition & 1 deletion device-api/bin/list_npu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async fn main() -> Result<(), DeviceError> {
let mut rows = vec![];

for device in found.iter() {
let uuid = device.device_uuid().unwrap_or_default();
let uuid = device.device_uuid();
let status = device.get_status_all().await?;
let mut status: Vec<(u8, _)> = status.into_iter().collect();
status.sort_by(|a, b| a.0.cmp(&b.0));
Expand Down
41 changes: 0 additions & 41 deletions device-api/src/arch.rs

This file was deleted.

Loading

0 comments on commit eebaf7e

Please sign in to comment.