Device Initialization and CleanupΒΆ

The first HAL operations to implement are device initialization (device_create) and cleanup (device_free), which are performed in hal_main.cpp. These operations create and destroy refsi_hal_device instances, respectively. Another task is opening the RefSi device through the refsidrv driver in order to retrieve a refsi_device_t handle, which can be used to control the device.

First, we will add a field to refsi_hal_device to store this handle and add a corresponding parameter to the constructor. We will also add an initialize function that can be used to perform device initialization tasks that can fail:

// refsi_hal.h
#include "hal.h"
#include "hal_riscv.h"
#include "refsidrv/refsidrv.h"   // Added

class refsi_hal_device : public hal::hal_device_t {
 public:
  refsi_hal_device(riscv::hal_device_info_riscv_t *info, refsi_device_t device,
                   std::recursive_mutex &hal_lock);
  bool initialize();             // Added
  ...
 private:
  std::recursive_mutex &hal_lock;
  hal::hal_device_info_t *info;
  refsi_device_t device;         // Added
};

The definition of the constructor needs to be updated as well. In particular, the device field needs to be initialized using the new parameter:

// refsi_hal.cpp
refsi_hal_device::refsi_hal_device(riscv::hal_device_info_riscv_t *info,
                                   refsi_device_t device,
                                   std::recursive_mutex &hal_lock)
    : hal::hal_device_t(info), hal_lock(hal_lock), device(device) {
}

The device_create function needs to call the driver to open the device and retrieve a device handle. Once we have this handle, a refsi_hal_device object can be created. If device initialization success, the handle will be returned:

// hal_main.cpp
hal::hal_device_t *device_create(uint32_t index) override {
  refsi_locker locker(lock);
  if (!initialized || (index > 0)) {
    return nullptr;
  }
  refsi_device_t device = refsiOpenDevice(REFSI_M);
  if (!device) {
    return nullptr;
  }
  refsi_hal_device *hal_device = new refsi_hal_device(&hal_device_info,
                                                      device, lock);
  if (!hal_device->initialize()) {
    delete hal_device;
    return nullptr;
  }
  return hal_device;
}

bool refsi_hal_device::initialize() {
  return true;
}

The counterpart to device_create is device_delete, which simply calls the refsi_hal_device destructor:

// hal_main.cpp
bool device_delete(hal::hal_device_t *device) override {
  // No locking - this is done by refsi_hal_device's destructor.
  delete static_cast<refsi_hal_device *>(device);
  return device != nullptr;
}

Finally, destroying a refsi_hal_device object should release the device handle and shut down the device:

// refsi_hal.h
class refsi_hal_device : public hal::hal_device_t {
 public:
  virtual ~refsi_hal_device();
};
// refsi_hal.cpp
refsi_hal_device::~refsi_hal_device() {
  refsi_locker locker(hal_lock);
  refsiShutdownDevice(device);
}

At this point, running clik examples results in a different error than we have previously seen:

$ bin/copy_buffer
  Using device 'RefSi M1 Tutorial'
  Could not create buffers.
$ bin/hello
  Using device 'RefSi M1 Tutorial'
  Unable to create a program from the kernel binary.
$ bin/vector_add
  Using device 'RefSi M1 Tutorial'
  Unable to create a program from the kernel binary.

We can see that the device was created successfully and that the clik example was able to query its name (RefSi M1 Tutorial). The new errors show that different HAL operations (mem_alloc and program_load) are failing due to being unimplemented. We will explain how to do so in future sections.