301 lines
7.3 KiB
C++
301 lines
7.3 KiB
C++
/*
|
|
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "jedParser.hpp"
|
|
|
|
/* GGM: TODO
|
|
* - use NOTE for Lxxx
|
|
* - be less lattice compliant
|
|
*/
|
|
|
|
using namespace std;
|
|
|
|
JedParser::JedParser(string filename, bool verbose):
|
|
ConfigBitstreamParser(filename, ConfigBitstreamParser::BIN_MODE),
|
|
_fuse_count(0), _pin_count(0), _featuresRow(0), _feabits(0), _checksum(0),
|
|
_userCode(0), _security_settings(0), _default_fuse_state(0)
|
|
{
|
|
}
|
|
|
|
/* fill a vector with consecutive lines until '*'
|
|
*/
|
|
vector<string> JedParser::readJEDLine()
|
|
{
|
|
string buffer;
|
|
vector<string> lines;
|
|
bool inLine = true;
|
|
|
|
do {
|
|
std::getline(_fd, buffer, '\n');
|
|
if (buffer.size() == 0)
|
|
break;
|
|
|
|
if (buffer[buffer.size()-1] == '*') {
|
|
inLine = false;
|
|
buffer.pop_back();
|
|
}
|
|
lines.push_back(buffer);
|
|
} while (inLine);
|
|
return lines;
|
|
}
|
|
|
|
/* convert one serie ASCII 1/0 to a vector of
|
|
* unsigned char
|
|
*/
|
|
void JedParser::buildDataArray(const string &content, struct jed_data &jed)
|
|
{
|
|
size_t data_len = content.size();
|
|
string tmp_buff;
|
|
uint8_t data = 0;
|
|
for (size_t i = 0; i < content.size(); i+=8) {
|
|
data = 0;
|
|
for (int ii = 0; ii < 8; ii++) {
|
|
uint8_t val = (content[i+ii] == '1'?1:0);
|
|
data |= val << ii;
|
|
}
|
|
tmp_buff += data;
|
|
}
|
|
jed.data.push_back(std::move(tmp_buff));
|
|
jed.len += data_len;
|
|
}
|
|
|
|
void JedParser::display()
|
|
{
|
|
printf("feabits :\n");
|
|
printf("%04x <-> %d\n", _feabits, _feabits);
|
|
/* 15-14: always 0 */
|
|
printf("\tBoot Mode : ");
|
|
switch ((_feabits>>11)&0x07) {
|
|
case 0:
|
|
printf("Single Boot from Configuration Flash\n");
|
|
break;
|
|
case 1:
|
|
printf("Dual Boot from Configuration Flash then External if there is a failure\n");
|
|
break;
|
|
case 3:
|
|
printf("Single Boot from External Flash\n");
|
|
break;
|
|
default:
|
|
printf("Error\n");
|
|
}
|
|
|
|
printf("\tMaster Mode SPI : %s\n",
|
|
(((_feabits>>11)&0x01)?"enable":"disable"));
|
|
printf("\tI2c port : %s\n",
|
|
(((_feabits>>10)&0x01)?"disable":"enable"));
|
|
printf("\tSlave SPI port : %s\n",
|
|
(((_feabits>>9)&0x01)?"disable":"enable"));
|
|
printf("\tJTAG port : %s\n",
|
|
(((_feabits>>8)&0x01)?"disable":"enable"));
|
|
printf("\tDONE : %s\n",
|
|
(((_feabits>>7)&0x01)?"enable":"disable"));
|
|
printf("\tINITN : %s\n",
|
|
(((_feabits>>6)&0x01)?"enable":"disable"));
|
|
printf("\tPROGRAMN : %s\n",
|
|
(((_feabits>>5)&0x01)?"disable":"enable"));
|
|
printf("\tMy_ASSP : %s\n",
|
|
(((_feabits>>4)&0x01)?"enable":"disable"));
|
|
/* 3-0: always 0 */
|
|
|
|
printf("Pin Count : %d\n", _pin_count);
|
|
printf("Fuse Count : %d\n", _fuse_count);
|
|
}
|
|
|
|
/* E field, for latice contains two sub-field
|
|
* 1: Exxxx\n : feature Row
|
|
* 2: yyyy*\n : feabits
|
|
*/
|
|
void JedParser::parseEField(vector<string> content)
|
|
{
|
|
_featuresRow = 0;
|
|
string featuresRow = content[0].substr(1);
|
|
for (size_t i = 0; i < featuresRow.size(); i++)
|
|
_featuresRow |= ((featuresRow[i] - '0') << i);
|
|
string feabits = content[1];
|
|
_feabits = 0;
|
|
for (size_t i = 0; i < feabits.size(); i++) {
|
|
_feabits |= ((feabits[i] - '0') << i);
|
|
}
|
|
}
|
|
|
|
void JedParser::parseLField(vector<string> content)
|
|
{
|
|
int start_offset;
|
|
sscanf(content[0].substr(1).c_str(), "%d", &start_offset);
|
|
/* two possibilities
|
|
* current line finish with '*' : Lxxxx YYYYY*<EOF>
|
|
* or current line is only offset and next(s) line(s) are data :
|
|
* Lxxxx<EOF>
|
|
*/
|
|
struct jed_data d;
|
|
string buffer;
|
|
d.offset = start_offset;
|
|
d.len = 0;
|
|
if (content.size() > 1) {
|
|
for (size_t i = 1; i < content.size(); i++) {
|
|
if (content[i].size() != 0)
|
|
buildDataArray((content[i]), d);
|
|
}
|
|
} else {
|
|
// search space
|
|
std::istringstream iss(content[0]);
|
|
vector<string> myList((std::istream_iterator<string>(iss)),
|
|
std::istream_iterator<string>());
|
|
myList[1].pop_back();
|
|
buildDataArray(myList[1], d);
|
|
}
|
|
_data_list.push_back(std::move(d));
|
|
}
|
|
|
|
int JedParser::parse()
|
|
{
|
|
string previousNote;
|
|
|
|
if (!_fd.is_open()) {
|
|
_fd.open(_filename);
|
|
if (!_fd.is_open()) {
|
|
cerr << "error to opening jed file " << _filename << endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
string content;
|
|
|
|
_fd.seekg(0, _fd.beg);
|
|
|
|
/* First line must STX (0x02) */
|
|
std::getline(_fd, content, '\n');
|
|
if (content[0] != 0x02) {
|
|
printf("wrong file\n");
|
|
return 0;
|
|
}
|
|
|
|
/* read full content
|
|
* JED file end fix ETX (0x03) + file checksum + \n
|
|
*/
|
|
std::vector<string>lines;
|
|
do {
|
|
lines = readJEDLine();
|
|
if (lines.size() == 0)
|
|
break;
|
|
|
|
switch (lines[0][0]) {
|
|
case 'N': // note
|
|
previousNote = lines[0].substr(5);
|
|
break;
|
|
case 'Q':
|
|
int count;
|
|
sscanf(lines[0].c_str()+2, "%d", &count);
|
|
switch (lines[0][1]) {
|
|
case 'F': // fuse count
|
|
_fuse_count = count;
|
|
break;
|
|
case 'P': // pin count
|
|
_pin_count = count;
|
|
break;
|
|
default:
|
|
cerr << "Error for 'Q' unknown qualifier " << lines[1] << endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'G':
|
|
_security_settings = static_cast<uint8_t>(lines[0][1]) - '0';
|
|
break;
|
|
case 'F':
|
|
_default_fuse_state = lines[0][1] - '0';
|
|
break;
|
|
case 'C':
|
|
sscanf(lines[0].c_str() + 1, "%hx", &_checksum);
|
|
break;
|
|
case 0x03:
|
|
if (_verbose)
|
|
cout << "end" << endl;
|
|
break;
|
|
case 'E':
|
|
parseEField(lines);
|
|
break;
|
|
case 'L': // fuse offset
|
|
parseLField(lines);
|
|
_data_list[_data_list.size()-1].associatedPrevNote = previousNote;
|
|
break;
|
|
case 'U': // userCode
|
|
switch (lines[0][1]) {
|
|
case 'H': /* hex */
|
|
sscanf(lines[0].c_str() + 2, "%x", &_userCode);
|
|
break;
|
|
case 'A': /* ASCII */
|
|
sscanf(lines[0].c_str() + 2, "%d", &_userCode);
|
|
break;
|
|
default: /* binary */
|
|
for (size_t ii = 1; ii < lines[0].size(); ii++)
|
|
_userCode = ((_userCode << 1) | (lines[0][ii] - '0'));
|
|
}
|
|
break;
|
|
default:
|
|
printf("inconnu\n");
|
|
cout << lines[0]<< endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
} while (lines[0][0] != 0x03);
|
|
|
|
int size = 0;
|
|
for (size_t i = 0; i < _data_list.size(); i++) {
|
|
if (_verbose) {
|
|
printf("area[%ld] %d %d ", i, _data_list[i].offset, _data_list[i].len);
|
|
printf("%s\n", _data_list[i].associatedPrevNote.c_str());
|
|
}
|
|
size += _data_list[i].len;
|
|
}
|
|
|
|
uint16_t checksum = 0;
|
|
for (size_t line = 0; line < _data_list[0].data.size(); line++) {
|
|
for (size_t col = 0; col < _data_list[0].data[line].size(); col++)
|
|
checksum += (uint8_t)_data_list[0].data[line][col];
|
|
}
|
|
|
|
if (_verbose)
|
|
printf("theorical checksum %x -> %x\n", _checksum, checksum);
|
|
if (_checksum != checksum) {
|
|
cerr << "Error: wrong checksum" << endl;
|
|
return 0;
|
|
}
|
|
|
|
if (_verbose)
|
|
printf("array size %ld\n", _data_list[0].data.size());
|
|
|
|
if (_fuse_count != size) {
|
|
cerr << "Not all fuses are programmed" << endl;
|
|
return 0;
|
|
}
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|