From dbe4e4e4725efceb626f612d8ef8d45499518baf Mon Sep 17 00:00:00 2001 From: Niklas Eiling Date: Mon, 8 Jan 2024 10:44:17 +0100 Subject: [PATCH] add i2c configuration logic to dino IP Signed-off-by: Niklas Eiling --- fpga/include/villas/fpga/ips/dino.hpp | 70 +++++++++++++--- fpga/lib/ips/dino.cpp | 116 +++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 14 deletions(-) diff --git a/fpga/include/villas/fpga/ips/dino.hpp b/fpga/include/villas/fpga/ips/dino.hpp index 152dd2d65..0c83fe4a5 100644 --- a/fpga/include/villas/fpga/ips/dino.hpp +++ b/fpga/include/villas/fpga/ips/dino.hpp @@ -1,12 +1,15 @@ /* Driver for wrapper around Dino * + * Author: Niklas Eiling * Author: Steffen Vogel + * SPDX-FileCopyrightText: 2024 Niklas Eiling * SPDX-FileCopyrightText: 2020 Institute for Automation of Complex Power Systems, RWTH Aachen University * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include #include namespace villas { @@ -15,18 +18,65 @@ namespace ip { class Dino : public Node { public: - static constexpr const char* masterPort = "m_axis"; - static constexpr const char* slavePort = "s_axis"; + union IoextPorts { + struct __attribute__((packed)) { + bool clk_dir : 1; + bool data_dir : 1; + bool status_led : 1; + bool n_we : 1; // write enable (active low) + bool input_zero : 1; + bool sat_detect : 1; + bool gain_lsb : 1; + bool gain_msb : 1; + } fields; + uint8_t raw; + }; + enum Gain { GAIN_1 = 0, GAIN_2 = 1, GAIN_5 = 2, GAIN_10 = 3 }; - const StreamVertex& getDefaultSlavePort() const - { - return getSlavePort(slavePort); - } + Dino(); + virtual ~Dino(); + void setI2c(std::shared_ptr i2cdev, uint8_t i2c_channel) { + this->i2cdev = i2cdev; + this->i2c_channel = i2c_channel; + } + virtual void configureHardware() = 0; - const StreamVertex& getDefaultMasterPort() const - { - return getMasterPort(masterPort); - } + static constexpr const char *masterPort = "M00_AXIS"; + static constexpr const char *slavePort = "S00_AXIS"; + + const StreamVertex &getDefaultSlavePort() const { + return getSlavePort(slavePort); + } + + const StreamVertex &getDefaultMasterPort() const { + return getMasterPort(masterPort); + } + + IoextPorts getIoextDir(); + IoextPorts getIoextOut(); + +protected: + std::shared_ptr i2cdev; + uint8_t i2c_channel; + + void setIoextDir(IoextPorts ports); + void setIoextOut(IoextPorts ports); +}; + +class DinoAdc : public Dino { +public: + DinoAdc(); + virtual ~DinoAdc(); + virtual void configureHardware() override; +}; + +class DinoDac : public Dino { +public: + DinoDac(); + virtual ~DinoDac(); + virtual void configureHardware() override; + void setGain(Gain gain); + Gain getGain(); }; } // namespace ip diff --git a/fpga/lib/ips/dino.cpp b/fpga/lib/ips/dino.cpp index 22f331e76..b1b65dc16 100644 --- a/fpga/lib/ips/dino.cpp +++ b/fpga/lib/ips/dino.cpp @@ -13,7 +13,115 @@ using namespace villas::fpga::ip; -static char n[] = "dino"; -static char d[] = "Analog and Digital IO"; -static char v[] = "xilinx.com:module_ref:dino:"; -static NodePlugin f; +Dino::Dino() : Node(), i2cdev(nullptr), i2c_channel(0) {} + +Dino::~Dino() {} + +void Dino::setIoextDir(IoextPorts ports) { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + std::vector data = {I2C_IOEXT_REG_DIR, ports.raw}; + i2cdev->write(I2C_IOEXT_ADDR, data); +} + +void Dino::setIoextOut(IoextPorts ports) { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + std::vector data = {I2C_IOEXT_REG_OUT, ports.raw}; + i2cdev->write(I2C_IOEXT_ADDR, data); +} + +Dino::IoextPorts Dino::getIoextDir() { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + std::vector data = {I2C_IOEXT_REG_DIR}; + i2cdev->write(I2C_IOEXT_ADDR, data); + i2cdev->read(I2C_IOEXT_ADDR, data, 1); + IoextPorts ports; + ports.raw = data[0]; + return ports; +} + +Dino::IoextPorts Dino::getIoextOut() { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + std::vector data = {I2C_IOEXT_REG_OUT}; + i2cdev->write(I2C_IOEXT_ADDR, data); + i2cdev->read(I2C_IOEXT_ADDR, data, 1); + IoextPorts ports; + ports.raw = data[0]; + return ports; +} + +DinoAdc::DinoAdc() : Dino() {} + +DinoAdc::~DinoAdc() {} + +void DinoAdc::configureHardware() { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + i2cdev->getSwitch().setChannel(i2c_channel); + IoextPorts ioext = {.raw = 0}; + ioext.fields.sat_detect = true; + setIoextDir(ioext); + ioext.raw = 0; + ioext.fields.data_dir = true; + ioext.fields.status_led = true; + ioext.fields.n_we = true; + ioext.fields.input_zero = true; + setIoextOut(ioext); + ioext.fields.n_we = false; + setIoextOut(ioext); +} + +DinoDac::DinoDac() : Dino() {} + +DinoDac::~DinoDac() {} + +void DinoDac::configureHardware() { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + i2cdev->getSwitch().setChannel(i2c_channel); + IoextPorts ioext = {.raw = 0}; + setIoextDir(ioext); + ioext.fields.status_led = true; + setIoextOut(ioext); + setGain(GAIN_1); +} + +void DinoDac::setGain(Gain gain) { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + i2cdev->getSwitch().setChannel(i2c_channel); + IoextPorts ioext = getIoextOut(); + ioext.fields.gain_lsb = gain & 0x1; + ioext.fields.gain_msb = gain & 0x2; + setIoextOut(ioext); +} + +Dino::Gain DinoDac::getGain() { + if (i2cdev == nullptr) { + throw RuntimeError("I2C device not set"); + } + i2cdev->getSwitch().setChannel(i2c_channel); + IoextPorts ioext = getIoextOut(); + return static_cast((ioext.fields.gain_msb << 1) | + ioext.fields.gain_lsb); +} + +static char n_adc[] = "DINO ADC"; +static char d_adc[] = "DINO analog to digital converter"; +static char v_adc[] = "xilinx.com:module_ref:dinoif_fast:"; +static NodePlugin f_adc; + +static char n_dac[] = "DINO DAC"; +static char d_dac[] = "DINO digital to analog converter"; +static char v_dac[] = "xilinx.com:module_ref:dinoif_dac:"; +static NodePlugin f_dac;