]> git.sesse.net Git - pistorm/blob - i2c_updater/pi-i2c/src/bitstream.cpp
Initial work on FPGA I2C programmer
[pistorm] / i2c_updater / pi-i2c / src / bitstream.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 "bitstream.hpp"
6 #include <sstream>
7 #include <iomanip>
8 #include "file.hpp"
9
10 // Based on https://prjtrellis.readthedocs.io/en/latest/architecture/bitstream_format.html ,
11 // Lattice Diamond programmer operation's I2C dump and "ECP5 and ECP5-5G sysCONFIG Usage Guide"
12
13 #define SECTION_ASCII_COMMENTS_BEGIN 0xFF00
14 #define SECTION_PREAMBLE_WORD_1 0xFFFF
15 #define SECTION_PREAMBLE_WORD_2 0xBDB3
16
17 struct tBitstreamCmd {
18         enum class tId: uint8_t {
19                 LSC_PROG_CNTRL0      = 0x22,
20                 LSC_RESET_CRC        = 0x3B,
21                 LSC_INIT_ADDRESS     = 0x46,
22                 ISC_PROGRAM_DONE     = 0x5E,
23                 LSC_PROG_INCR_RTI    = 0x82,
24                 LSC_PROG_SED_CRC     = 0xA2,
25                 LSC_EBR_WRITE        = 0xB2,
26                 ISC_PROGRAM_USERCODE = 0xC2,
27                 ISC_PROGRAM_SECURITY = 0xCE,
28                 LSC_VERIFY_ID        = 0xE2,
29                 LSC_EBR_ADDRESS      = 0xF6,
30                 ISC_NOOP             = 0xFF,
31         };
32
33         tBitstreamCmd::tId m_eId;
34         std::array<uint8_t, 3> m_Operands;
35         std::vector<uint8_t> m_vData;
36
37         tBitstreamCmd(std::ifstream &FileIn);
38 };
39
40 tBitstream::tBitstream(const std::string &FilePath)
41 {
42         std::ifstream FileIn;
43         FileIn.open(FilePath.c_str(), std::ifstream::in | std::ifstream::binary);
44         if(!FileIn.good()) {
45                 throw std::runtime_error("Couldn't open file");
46         }
47
48         uint16_t uwSectionId;
49         nFile::readBigEndian(FileIn, uwSectionId);
50         if(uwSectionId == SECTION_ASCII_COMMENTS_BEGIN) {
51                 // Read comments
52                 std::string Comment;
53                 char c;
54                 while(!FileIn.eof()) {
55                         nFile::readBigEndian(FileIn, c);
56                         if(c == '\xFF') {
57                                 break;
58                         }
59                         if(c == '\0') {
60                                 m_vComments.push_back(Comment);
61                                 Comment.clear();
62                         }
63                         else {
64                                 Comment += c;
65                         }
66                 }
67
68                 // Continue reading until we hit preamble
69                 nFile::readBigEndian(FileIn, uwSectionId);
70         }
71
72         if(uwSectionId != SECTION_PREAMBLE_WORD_1) {
73                 // I want std::format so bad :(
74                 std::stringstream ss;
75                 ss << "Unexpected bitstream section: " << std::setfill('0') <<
76                         std::setw(4) << std::ios::hex << uwSectionId;
77                 throw std::runtime_error(ss.str());
78         }
79
80         // Read final part of preamble
81         uint16_t uwPreambleEnd;
82         nFile::readBigEndian(FileIn, uwPreambleEnd);
83         if(uwPreambleEnd != SECTION_PREAMBLE_WORD_2) {
84                 // I want std::format so bad :(
85                 std::stringstream ss;
86                 ss << "Unexpected preamble ending" << std::setfill('0') <<
87                         std::setw(4) << std::ios::hex << uwPreambleEnd;
88                 throw std::runtime_error(ss.str());
89         }
90
91         while(!FileIn.eof()) {
92                 // Read bitstream commands
93                 tBitstreamCmd Cmd(FileIn);
94                 if(FileIn.eof()) {
95                         // Last read haven't been successfull - no more data
96                         break;
97                 }
98
99                 // Read extra data, if any
100                 switch(Cmd.m_eId) {
101                         case tBitstreamCmd::tId::ISC_NOOP:
102                         case tBitstreamCmd::tId::LSC_RESET_CRC:
103                         case tBitstreamCmd::tId::LSC_INIT_ADDRESS:
104                                 // No additional data
105                                 break;
106                         case tBitstreamCmd::tId::LSC_VERIFY_ID:
107                                 nFile::readData(FileIn, m_DeviceId.data(), m_DeviceId.size());
108                                 break;
109                         case tBitstreamCmd::tId::LSC_PROG_CNTRL0:
110                                 nFile::readBigEndian(FileIn, m_ulCtlReg0);
111                                 break;
112                         case tBitstreamCmd::tId::LSC_PROG_INCR_RTI: {
113                                 // Store program data
114                                 // bool isCrcVerify = Cmd.m_Operands[0] & 0x80;
115                                 // bool isCrcAtEnd = !(Cmd.m_Operands[0] & 0x40);
116                                 // bool isDummyBits = !(Cmd.m_Operands[0] & 0x20);
117                                 // bool isDummyBytes = !(Cmd.m_Operands[0] & 0x10);
118                                 auto DummyBytesCount = Cmd.m_Operands[0] & 0xF;
119                                 uint16_t uwRowCount = (Cmd.m_Operands[1] << 8) | Cmd.m_Operands[2];
120
121                                 for(auto RowIdx = 0; RowIdx < uwRowCount; ++RowIdx) {
122                                         // TODO: where to get the size of each data row? From comment?
123                                         std::vector<uint8_t> vRow;
124                                         vRow.resize(26);
125                                         uint16_t uwCrc;
126                                         nFile::readData(FileIn, vRow.data(), vRow.size());
127                                         nFile::readBigEndian(FileIn, uwCrc);
128                                         if(uwCrc == 0) {
129                                                 // I want std::format so bad :(
130                                                 std::stringstream ss;
131                                                 ss << "Empty CRC near pos " << std::hex << FileIn.tellg();
132                                                 throw std::runtime_error(ss.str());
133                                         }
134                                         m_vProgramData.push_back(vRow);
135                                         FileIn.seekg(DummyBytesCount, std::ios::cur);
136                                 }
137                         } break;
138                         case tBitstreamCmd::tId::ISC_PROGRAM_USERCODE:
139                                 // Read usercode, skip CRC
140                                 nFile::readData(FileIn, m_UserCode.data(), m_UserCode.size());
141                                 FileIn.seekg(2, std::ios::cur);
142                                 break;
143                         case tBitstreamCmd::tId::ISC_PROGRAM_DONE:
144                                 break;
145                         default: {
146                                 // I want std::format so bad :(
147                                 std::stringstream ss;
148                                 ss << "Unhandled bitstream cmd near file pos 0x" << std::hex <<
149                                         FileIn.tellg() << ": 0x" << std::setw(2) << std::setfill('0') <<
150                                         int(Cmd.m_eId);
151                                 throw std::runtime_error(ss.str());
152                         }
153                 }
154         }
155 }
156
157 tBitstreamCmd::tBitstreamCmd(std::ifstream &FileIn)
158 {
159         uint32_t ulCmdRaw;
160         nFile::readBigEndian(FileIn, ulCmdRaw);
161         m_eId = tBitstreamCmd::tId(ulCmdRaw >> 24);
162         m_Operands[0] = (ulCmdRaw >> 16) & 0xFF;
163         m_Operands[1] = (ulCmdRaw >> 8) & 0xFF;
164         m_Operands[2] = (ulCmdRaw >> 0) & 0xFF;
165 }