diff --git a/fpga/include/villas/fpga/ips/i2c.hpp b/fpga/include/villas/fpga/ips/i2c.hpp index 8a9d1e626..f587c391e 100644 --- a/fpga/include/villas/fpga/ips/i2c.hpp +++ b/fpga/include/villas/fpga/ips/i2c.hpp @@ -22,22 +22,29 @@ class I2c : public Node { public: friend class I2cFactory; + I2c(); virtual ~I2c(); virtual bool init() override; bool reset() override; - bool write(std::list &data); - bool read(std::list &data, size_t max_read); + bool write(u8 address, std::vector &data); + bool read(u8 address, std::vector &data, size_t max_read); + + int transmitIntrs; + int receiveIntrs; + int statusIntrs; private: static constexpr char registerMemory[] = "Reg"; + static constexpr char i2cInterrupt[] = "iic2intc_irpt"; XIic xIic; XIic_Config xConfig; std::mutex hwLock; - bool configDone = false; - + bool configDone; + bool polling; +}; class I2cFactory : NodeFactory { public: @@ -53,16 +60,14 @@ private: } // Create a concrete IP instance - Core *make() const { return new Dma; }; + Core *make() const { return new I2c; }; protected: virtual void parse(Core &ip, json_t *json) override; - virtual void configurePollingMode(Core &ip, PollingMode mode) override { - dynamic_cast(ip).polling = (mode == POLL); + dynamic_cast(ip).polling = (mode == POLL); } }; - } // namespace ip } // namespace fpga } // namespace villas diff --git a/fpga/lib/CMakeLists.txt b/fpga/lib/CMakeLists.txt index 4ae2f2547..da18c1e2c 100644 --- a/fpga/lib/CMakeLists.txt +++ b/fpga/lib/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES ips/rtds.cpp ips/switch.cpp ips/timer.cpp + ips/i2c.cpp ips/rtds2gpu/rtds2gpu.cpp ips/rtds2gpu/xrtds2gpu.c diff --git a/fpga/lib/ips/i2c.cpp b/fpga/lib/ips/i2c.cpp index 2ed35e062..9e0ebe97b 100644 --- a/fpga/lib/ips/i2c.cpp +++ b/fpga/lib/ips/i2c.cpp @@ -15,17 +15,137 @@ using namespace villas::fpga::ip; -I2c::I2c() : Node("i2c") {} +I2c::I2c() + : Node(), transmitIntrs(0), receiveIntrs(0), statusIntrs(0), xIic(), + xConfig(), hwLock(), configDone(false), polling(false) {} I2c::~I2c() {} -bool I2c::init() override {} +static void SendHandler(I2c *i2c, __attribute__((unused)) int bytesSend) { + i2c->transmitIntrs++; +} +static void ReceiveHandler(I2c *i2c, __attribute__((unused)) int bytesSend) { + i2c->receiveIntrs++; +} +static void StatusHandler(I2c *i2c, __attribute__((unused)) int event) { + i2c->statusIntrs++; +} -bool I2c::reset() override {} +bool I2c::init() { + int ret; + if (!configDone) { + throw RuntimeError("I2C configuration not done"); + } + ret = XIic_CfgInitialize(&xIic, &xConfig, xConfig.BaseAddress); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to initialize I2C"); + } -bool I2c::write(std::list &data) {} + XIic_SetSendHandler(&xIic, this, (XIic_Handler)SendHandler); + XIic_SetRecvHandler(&xIic, this, (XIic_Handler)ReceiveHandler); + XIic_SetStatusHandler(&xIic, this, (XIic_StatusHandler)StatusHandler); -bool I2c::read(std::list &data, size_t max_read) {} + irqs[i2cInterrupt].irqController->enableInterrupt(irqs[i2cInterrupt], + polling); + + return true; +} + +bool I2c::reset() { return true; } + +bool I2c::write(u8 address, std::vector &data) { + int ret; + + ret = + XIic_SetAddress(&xIic, XII_ADDR_TO_SEND_TYPE, static_cast(address)); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to set I2C address"); + } + + transmitIntrs = 0; + xIic.Stats.TxErrors = 0; + + int retries = 10; + while (xIic.Stats.TxErrors != 0 && retries > 0) { + ret = XIic_Start(&xIic); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to start I2C"); + } + + ret = XIic_MasterSend(&xIic, data.data(), data.size()); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to send I2C data"); + } + int intrRetries = 10; + while (transmitIntrs == 0 && intrRetries > 0) { + irqs[i2cInterrupt].irqController->waitForInterrupt( + irqs[i2cInterrupt].num); + XIic_InterruptHandler(&xIic); + --intrRetries; + } + --retries; + } + + ret = XIic_Stop(&xIic); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to stop I2C"); + } + if (retries == 0) { + throw RuntimeError("Failed to send I2C data"); + } + return true; +} + +bool I2c::read(u8 address, std::vector &data, size_t max_read) { + int ret; + + data.resize(data.size() + max_read); + u8 *dataPtr = data.data() + data.size() - max_read; + + ret = + XIic_SetAddress(&xIic, XII_ADDR_TO_SEND_TYPE, static_cast(address)); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to set I2C address"); + } + + receiveIntrs = 0; + + ret = XIic_Start(&xIic); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to start I2C"); + } + + int retries = 10; + while (xIic.Stats.TxErrors != 0 && retries > 0) { + ret = XIic_Start(&xIic); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to start I2C"); + } + + ret = XIic_MasterRecv(&xIic, dataPtr, max_read); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to receive I2C data"); + } + int intrRetries = 10; + while (receiveIntrs == 0 && intrRetries > 0) { + irqs[i2cInterrupt].irqController->waitForInterrupt( + irqs[i2cInterrupt].num); + XIic_InterruptHandler(&xIic); + --intrRetries; + } + --retries; + } + + ret = XIic_Stop(&xIic); + if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to stop I2C"); + } + if (retries == 0) { + throw RuntimeError("Failed to send I2C data"); + } + + return XST_SUCCESS; +} void I2cFactory::parse(Core &ip, json_t *cfg) { NodeFactory::parse(ip, cfg); @@ -33,17 +153,27 @@ void I2cFactory::parse(Core &ip, json_t *cfg) { auto &i2c = dynamic_cast(ip); int i2c_frequency = 0; + char *component_name = nullptr; json_error_t err; int ret = json_unpack_ex( cfg, &err, 0, "{ s: { s?: i, s?: i, s?: i, s?: i, s?: i} }", "parameters", "c_iic_freq", &i2c_frequency, "c_ten_bit_adr", &i2c.xConfig.Has10BitAddr, - "c_gpo_width", &i2c.xConfig.GpOutWidth, "component_name", - &i2c.xConfig.Name, "c_baseaddr", &i2c.xConfig.BaseAddress); - if (ret != 0) + "c_gpo_width", &i2c.xConfig.GpOutWidth, "component_name", &component_name, + "c_baseaddr", &i2c.xConfig.BaseAddress); + if (ret != 0) { throw ConfigError(cfg, err, "", "Failed to parse DMA configuration"); + } + if (component_name != nullptr) { + char last_letter = component_name[strlen(component_name) - 1]; + if (last_letter > '0' && last_letter <= '9') { + i2c.xConfig.DeviceId = atoi(&last_letter); + } else { + throw RuntimeError("Invalid device ID in component name"); + } + } - dma.configDone = true; + i2c.configDone = true; } static I2cFactory f;