]> git.sesse.net Git - pistorm/blob - i2c_updater/pi-i2c/src/main.cpp
Initial work on FPGA I2C programmer
[pistorm] / i2c_updater / pi-i2c / src / main.cpp
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <cstdio>
6 #include <string>
7 #include <optional>
8 #include <thread>
9 #include "bitstream.hpp"
10 #include "i2c.hpp"
11 #include "endian.hpp"
12
13 #define STATUS_BIT_BUSY (1 << 12)
14 #define STATUS_BIT_ERROR (1 << 13)
15
16 static bool waitForNotBusy(tI2c &I2c, uint8_t ubFpgaAddr)
17 {
18         using namespace std::chrono_literals;
19
20         uint32_t ulStatus;
21         do {
22                 std::this_thread::sleep_for(10ms);
23                 I2c.write(ubFpgaAddr, {0x3C, 0x00, 0x00 , 0x00});
24                 bool isRead = I2c.read(
25                         ubFpgaAddr, reinterpret_cast<uint8_t*>(&ulStatus), sizeof(ulStatus)
26                 );
27                 if(!isRead) {
28                         return false;
29                 }
30                 ulStatus = nEndian::bigToNative(ulStatus);
31                 if(ulStatus & STATUS_BIT_ERROR) {
32                         return false;
33                 }
34         } while(ulStatus & STATUS_BIT_BUSY);
35         return true;
36 }
37
38 static uint8_t reverseByte(uint8_t ubData) {
39         // https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
40         ubData = (ubData * 0x0202020202ULL & 0x010884422010ULL) % 1023;
41         return ubData;
42 }
43
44 int main(int lArgCount, const char *pArgs[])
45 {
46         using namespace std::chrono_literals;
47
48         if(lArgCount < 4) {
49                 printf("Usage:\n\t%s i2cPort i2cSlaveAddrHex /path/to/cfg.bit\n", pArgs[0]);
50                 printf("e.g.:\n\t%s /dev/i2c-1 5a file.bit\n", pArgs[0]);
51                 return EXIT_FAILURE;
52         }
53
54         std::optional<tBitstream> Bitstream;
55         try {
56                 Bitstream = tBitstream(pArgs[3]);
57         }
58         catch(const std::exception &Exc) {
59                 printf(
60                         "ERR: bitstream '%s' read fail: '%s'\n", pArgs[3], Exc.what()
61                 );
62                 return EXIT_FAILURE;
63         }
64
65         // Display some info about bitstream
66         printf("Successfully read bitstream:\n");
67         for(const auto &Comment: Bitstream->m_vComments) {
68                 printf("\t%s\n", Comment.c_str());
69         }
70
71         // Read the I2C address of the FPGA
72         auto FpgaAddr = std::stoi(pArgs[2], 0, 16);
73
74         // Initialize I2C port
75         std::optional<tI2c> I2c;
76         try {
77                 I2c = tI2c(pArgs[1]);
78         }
79         catch(const std::exception &Exc) {
80                 printf(
81                         "ERR: i2c port '%s' init fail: '%s'\n", pArgs[1], Exc.what()
82                 );
83                 return EXIT_FAILURE;
84         }
85
86         printf("Resetting FPGA...\n");
87         // TODO: CRESET:=0
88         std::this_thread::sleep_for(1s);
89         I2c->write(FpgaAddr, {0xA4, 0xC6, 0xF4, 0x8A});
90         // TODO: CRESET:=1
91         std::this_thread::sleep_for(10ms);
92
93         // 1. Read ID (E0)
94         printf("Checking device id...\n");
95         I2c->write(FpgaAddr, {0xE0, 0x00, 0x00, 0x00});
96         std::array<uint8_t, 4> DevId;
97         bool isRead = I2c->read(FpgaAddr, DevId);
98         if(!isRead) {
99                 printf("ERR: Can't read data from I2C device\n");
100                 return EXIT_FAILURE;
101         }
102         if(DevId != Bitstream->m_DeviceId) {
103                 printf(
104                         "ERR: DevId mismatch! Dev: %02X %02X %02X %02X, "
105                         ".bit: %02X, %02X, %02X, %02X\n",
106                         DevId[0], DevId[1], DevId[2], DevId[3],
107                         Bitstream->m_DeviceId[0], Bitstream->m_DeviceId[1],
108                         Bitstream->m_DeviceId[2], Bitstream->m_DeviceId[3]
109                 );
110                 return EXIT_FAILURE;
111         }
112
113         // 2. Enable configuration interface (C6)
114         printf("Initiating programming...\n");
115         I2c->write(FpgaAddr, {0xC6, 0x00, 0x00, 0x00});
116         std::this_thread::sleep_for(1ms);
117
118         // 3. Read status register (3C) and wait for busy bit go to 0
119         if(!waitForNotBusy(*I2c, FpgaAddr)) {
120                 printf("ERR: status register has error bit set!\n");
121                 return EXIT_FAILURE;
122         }
123
124         // 4. LSC_INIT_ADDRESS (46)
125         I2c->write(FpgaAddr, {0x46, 0x00, 0x00 , 0x00});
126         std::this_thread::sleep_for(100ms);
127
128         // 5. Program SRAM parts (82)
129         uint32_t lRowIdx;
130         for(const auto &Row: Bitstream->m_vProgramData) {
131                 printf(
132                         "\rProgramming row %5u/%5u...",
133                         ++lRowIdx, Bitstream->m_vProgramData.size()
134                 );
135                 fflush(stdout);
136                 // Compose and send next SRAM packet
137                 std::vector<uint8_t> Packet = {0x82, 0x21, 0x00, 0x00};
138                 for(const auto &RowByte: Row) {
139                         Packet.push_back(RowByte);
140                 }
141                 I2c->write(FpgaAddr, Packet);
142                 std::this_thread::sleep_for(100us);
143
144                 // Some kind of dummy packet
145                 I2c->write(FpgaAddr, {0x00, 0x00, 0x00, 0x00});
146                 std::this_thread::sleep_for(100us);
147         }
148         printf("\n");
149
150         // 6. Program usercode (C2)
151         printf("Programming usercode...\n");
152         I2c->write(FpgaAddr, {
153                 0x46, 0x00, 0x00 , 0x00,
154                 reverseByte(Bitstream->m_UserCode[3]), reverseByte(Bitstream->m_UserCode[2]),
155                 reverseByte(Bitstream->m_UserCode[1]), reverseByte(Bitstream->m_UserCode[0])
156         });
157
158         // 7. Program done (5E)
159         printf("Finalizing programming...\n");
160         I2c->write(FpgaAddr, {0x5E, 0x00, 0x00 , 0x00});
161
162         // 8. Read status register (3C) and wait for busy bit go to 0
163         if(!waitForNotBusy(*I2c, FpgaAddr)) {
164                 printf("ERR: status register has error bit set!\n");
165                 return EXIT_FAILURE;
166         }
167
168         // 9. Exit programming mode (26)
169         I2c->write(FpgaAddr, {0x26, 0x00, 0x00 , 0x00});
170
171         printf("All done!\n");
172         return EXIT_SUCCESS;
173 }