File: i2c_core_200.cpp

package info (click to toggle)
uhd 4.3.0.0%2Bds1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 175,384 kB
  • sloc: cpp: 271,145; ansic: 103,960; python: 95,906; vhdl: 55,838; tcl: 14,117; xml: 8,535; makefile: 2,706; sh: 2,432; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (161 lines) | stat: -rw-r--r-- 5,307 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//
// Copyright 2011-2012,2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
#include <uhdlib/usrp/cores/i2c_core_200.hpp>
#include <chrono>
#include <mutex>
#include <thread>

#define REG_I2C_WR_PRESCALER_LO (1 << 3) | 0
#define REG_I2C_WR_PRESCALER_HI (1 << 3) | 1
#define REG_I2C_WR_CTRL (1 << 3) | 2
#define REG_I2C_WR_DATA (1 << 3) | 3
#define REG_I2C_WR_CMD (1 << 3) | 4
#define REG_I2C_RD_DATA (0 << 3) | 3
#define REG_I2C_RD_ST (0 << 3) | 4

//
// STA, STO, RD, WR, and IACK bits are cleared automatically
//

#define I2C_CTRL_EN (1 << 7) // core enable
#define I2C_CTRL_IE (1 << 6) // interrupt enable

#define I2C_CMD_START (1 << 7) // generate (repeated) start condition
#define I2C_CMD_STOP (1 << 6) // generate stop condition
#define I2C_CMD_RD (1 << 5) // read from slave
#define I2C_CMD_WR (1 << 4) // write to slave
#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1)
#define I2C_CMD_RSVD_2 (1 << 2) // reserved
#define I2C_CMD_RSVD_1 (1 << 1) // reserved
#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt

#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK)
#define I2C_ST_BUSY \
    (1 << 6) // 1 after START signal detected; 0 after STOP signal detected
#define I2C_ST_AL (1 << 5) // Arbitration lost.  1 when core lost arbitration
#define I2C_ST_RSVD_4 (1 << 4) // reserved
#define I2C_ST_RSVD_3 (1 << 3) // reserved
#define I2C_ST_RSVD_2 (1 << 2) // reserved
#define I2C_ST_TIP (1 << 1) // Transfer-in-progress
#define I2C_ST_IP (1 << 0) // Interrupt pending

using namespace uhd;

i2c_core_200::~i2c_core_200(void)
{
    /* NOP */
}

class i2c_core_200_impl : public i2c_core_200
{
public:
    i2c_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t readback)
        : _iface(iface), _base(base), _readback(readback)
    {
        // init I2C FPGA interface.
        this->poke(REG_I2C_WR_CTRL, 0x0000);
        // set prescalers to operate at 400kHz: WB_CLK is 64MHz...
        static const uint32_t i2c_datarate = 400000;
        static const uint32_t wishbone_clk = 64000000; // FIXME should go somewhere else
        uint16_t prescaler                 = wishbone_clk / (i2c_datarate * 5) - 1;
        this->poke(REG_I2C_WR_PRESCALER_LO, prescaler & 0xFF);
        this->poke(REG_I2C_WR_PRESCALER_HI, (prescaler >> 8) & 0xFF);
        this->poke(REG_I2C_WR_CTRL, I2C_CTRL_EN); // enable I2C core
    }

    void write_i2c(uint16_t addr, const byte_vector_t& bytes) override
    {
        this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); // addr and read bit (0)
        this->poke(REG_I2C_WR_CMD,
            I2C_CMD_WR | I2C_CMD_START | (bytes.empty() ? I2C_CMD_STOP : 0));

        // wait for previous transfer to complete
        if (not wait_chk_ack()) {
            this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP);
            return;
        }

        for (size_t i = 0; i < bytes.size(); i++) {
            this->poke(REG_I2C_WR_DATA, bytes[i]);
            this->poke(REG_I2C_WR_CMD,
                I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0));
            if (!wait_chk_ack()) {
                this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP);
                return;
            }
        }
    }

    byte_vector_t read_i2c(uint16_t addr, size_t num_bytes) override
    {
        byte_vector_t bytes;
        if (num_bytes == 0)
            return bytes;

        while (this->peek(REG_I2C_RD_ST) & I2C_ST_BUSY) {
            /* NOP */
        }

        this->poke(REG_I2C_WR_DATA, (addr << 1) | 1); // addr and read bit (1)
        this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START);
        // wait for previous transfer to complete
        if (not wait_chk_ack()) {
            this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP);
        }
        for (size_t i = 0; i < num_bytes; i++) {
            this->poke(REG_I2C_WR_CMD,
                I2C_CMD_RD | ((num_bytes == i + 1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0));
            i2c_wait();
            bytes.push_back(this->peek(REG_I2C_RD_DATA));
        }
        return bytes;
    }

private:
    void i2c_wait(void)
    {
        for (size_t i = 0; i < 100; i++) {
            if ((this->peek(REG_I2C_RD_ST) & I2C_ST_TIP) == 0)
                return;
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
        UHD_LOGGER_ERROR("CORES") << "i2c_core_200: i2c_wait timeout";
    }

    bool wait_chk_ack(void)
    {
        i2c_wait();
        return (this->peek(REG_I2C_RD_ST) & I2C_ST_RXACK) == 0;
    }

    void poke(const size_t what, const uint8_t cmd)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _iface->poke32(_base, (what << 8) | cmd);
    }

    uint8_t peek(const size_t what)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _iface->poke32(_base, what << 8);
        return uint8_t(_iface->peek32(_readback));
    }

    wb_iface::sptr _iface;
    const size_t _base;
    const size_t _readback;
    std::mutex _mutex;
};

i2c_core_200::sptr i2c_core_200::make(
    wb_iface::sptr iface, const size_t base, const size_t readback)
{
    return sptr(new i2c_core_200_impl(iface, base, readback));
}