3 #include "rapidjson/filewritestream.h"
4 #include "rapidjson/prettywriter.h"
9 #include <system_error>
18 const std::map<std::string, std::string> LEGACY_TYPE_RENAMES = {{
"buffer",
"c_buffer"}, {
"carry",
"c_carry"}, {
"mux",
"c_mux"}, {
"lut",
"c_lut"}};
21 using ParameterSizes = std::vector<std::pair<std::string, u64>>;
23 const rapidjson::Value* find_member(
const rapidjson::Value&
object,
const char*
name)
25 if (!
object.IsObject())
29 auto it =
object.FindMember(
name);
30 return (it !=
object.MemberEnd()) ? &it->value :
nullptr;
33 std::string get_string(
const rapidjson::Value&
object,
const char*
name)
35 const rapidjson::Value* value = find_member(
object,
name);
36 return (value !=
nullptr && value->IsString()) ? value->GetString() :
"";
39 u64 get_uint(
const rapidjson::Value&
object,
const char*
name)
41 const rapidjson::Value* value = find_member(
object,
name);
42 return (value !=
nullptr && value->IsUint64()) ? value->GetUint64() : 0;
45 rapidjson::Value make_string(
const std::string& str, rapidjson::Document::AllocatorType& allocator)
47 return rapidjson::Value(str.c_str(), str.length(), allocator);
50 void collect_pins(
const rapidjson::Value& cell, std::vector<std::string>& input_pins, std::vector<std::string>& lut_output_pins)
52 const rapidjson::Value* pin_groups = find_member(cell,
"pin_groups");
53 if (pin_groups ==
nullptr || !pin_groups->IsArray())
58 for (
const auto& pin_group : pin_groups->GetArray())
60 const std::string
direction = get_string(pin_group,
"direction");
61 const rapidjson::Value*
pins = find_member(pin_group,
"pins");
62 if (
pins ==
nullptr || !
pins->IsArray())
67 for (
const auto& pin :
pins->GetArray())
71 input_pins.push_back(get_string(pin,
"name"));
73 else if (
direction ==
"output" && get_string(pin,
"type") ==
"lut")
75 lut_output_pins.push_back(get_string(pin,
"name"));
81 void rename_legacy_types(rapidjson::Value& cell, rapidjson::Document::AllocatorType& allocator)
83 if (!cell.HasMember(
"types") || !cell[
"types"].IsArray())
88 for (
auto&
type : cell[
"types"].GetArray())
94 if (
auto it = LEGACY_TYPE_RENAMES.find(
type.GetString()); it != LEGACY_TYPE_RENAMES.end())
96 type = make_string(it->second, allocator);
101 void upgrade_lut_config(rapidjson::Value& cell,
const std::string& cell_name, rapidjson::Document::AllocatorType& allocator, std::vector<std::string>& warnings)
103 rapidjson::Value& lut_config = cell[
"lut_config"];
104 if (!lut_config.IsObject() || lut_config.HasMember(
"output_pins") || !lut_config.HasMember(
"data_identifier"))
110 std::vector<std::string> input_pins;
111 std::vector<std::string> lut_output_pins;
112 collect_pins(cell, input_pins, lut_output_pins);
114 if (lut_output_pins.empty())
116 warnings.push_back(
"cell '" + cell_name +
"': no output pin with type 'lut' found, LUT configuration not migrated, please update manually.");
120 if (input_pins.empty())
122 warnings.push_back(
"cell '" + cell_name +
"': no input pins found, LUT configuration not migrated, please update manually.");
126 if (lut_output_pins.size() > 1)
129 warnings.push_back(
"cell '" + cell_name +
"': has " + std::to_string(lut_output_pins.size())
130 +
" LUT output pins, multi-output LUT cells cannot be migrated automatically, please update manually.");
134 const rapidjson::Value* bit_order = find_member(lut_config,
"bit_order");
135 const bool is_ascending = (bit_order ==
nullptr) || (bit_order->IsString() && std::string(bit_order->GetString()) ==
"ascending");
137 rapidjson::Value inputs(rapidjson::kArrayType);
138 for (
const auto& pin : input_pins)
140 inputs.PushBack(make_string(pin, allocator), allocator);
143 rapidjson::Value output_pin(rapidjson::kObjectType);
144 output_pin.AddMember(
"pin", make_string(lut_output_pins.front(), allocator), allocator);
145 output_pin.AddMember(
"data_identifier", rapidjson::Value(lut_config[
"data_identifier"], allocator), allocator);
146 output_pin.AddMember(
"bit_offset", 0, allocator);
147 output_pin.AddMember(
"bit_count",
u64(1) << input_pins.size(), allocator);
148 output_pin.AddMember(
"input_pins", inputs, allocator);
149 output_pin.AddMember(
"is_ascending", is_ascending, allocator);
151 rapidjson::Value output_pins(rapidjson::kArrayType);
152 output_pins.PushBack(output_pin, allocator);
154 rapidjson::Value new_lut_config(rapidjson::kObjectType);
155 if (bit_order !=
nullptr)
157 new_lut_config.AddMember(
"bit_order", rapidjson::Value(*bit_order, allocator), allocator);
159 if (
const rapidjson::Value* data_category = find_member(lut_config,
"data_category"); data_category !=
nullptr)
161 new_lut_config.AddMember(
"data_category", rapidjson::Value(*data_category, allocator), allocator);
163 new_lut_config.AddMember(
"output_pins", output_pins, allocator);
166 lut_config = new_lut_config;
169 u64* find_size(ParameterSizes& sizes,
const std::string&
identifier)
171 for (
auto& [ident,
size] : sizes)
183 if (
u64* existing = find_size(sizes,
identifier); existing !=
nullptr)
193 void add_parameters(rapidjson::Value& cell, rapidjson::Document::AllocatorType& allocator)
195 if (cell.HasMember(
"parameters"))
200 ParameterSizes sizes;
202 if (
const rapidjson::Value* lut_config = find_member(cell,
"lut_config"); lut_config !=
nullptr)
204 if (
const rapidjson::Value* output_pins = find_member(*lut_config,
"output_pins"); output_pins !=
nullptr && output_pins->IsArray())
206 for (
const auto& output_pin : output_pins->GetArray())
208 const std::string
identifier = get_string(output_pin,
"data_identifier");
209 const u64 required = get_uint(output_pin,
"bit_offset") + get_uint(output_pin,
"bit_count");
211 set_size(sizes,
identifier, (existing !=
nullptr) ? std::max(*existing, required) : required);
217 for (
const char* config_name : {
"ff_config",
"latch_config"})
219 if (
const rapidjson::Value* config = find_member(cell, config_name); config !=
nullptr && config->HasMember(
"data_identifier"))
221 const std::string
identifier = get_string(*config,
"data_identifier");
229 if (
const rapidjson::Value* ram_config = find_member(cell,
"ram_config"); ram_config !=
nullptr)
231 const rapidjson::Value* identifiers = find_member(*ram_config,
"data_identifiers");
232 if (identifiers !=
nullptr && identifiers->IsArray() && !identifiers->Empty() && ram_config->HasMember(
"bit_size"))
234 const u64 size = get_uint(*ram_config,
"bit_size") / identifiers->Size();
235 for (
const auto&
identifier : identifiers->GetArray())
247 rapidjson::Value parameters(rapidjson::kArrayType);
250 rapidjson::Value parameter(rapidjson::kObjectType);
251 parameter.AddMember(
"name", make_string(
identifier, allocator), allocator);
252 parameter.AddMember(
"type",
"bit_vector", allocator);
253 parameter.AddMember(
"size",
size, allocator);
254 parameter.AddMember(
"default",
"0x0", allocator);
255 parameters.PushBack(parameter, allocator);
257 cell.AddMember(
"parameters", parameters, allocator);
263 if (!document.IsObject())
265 return ERR(
"could not upgrade HGL document: document is not a JSON object");
268 std::vector<std::string> warnings;
271 if (document.HasMember(
"version"))
273 if (!document[
"version"].IsUint())
275 return ERR(
"could not upgrade HGL document: invalid version entry");
277 version = document[
"version"].GetUint();
287 return ERR(
"could not upgrade HGL document: version " + std::to_string(version) +
" uses a legacy pin format that cannot be upgraded automatically");
290 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
293 if (document.HasMember(
"cells") && document[
"cells"].IsArray())
295 for (
auto& cell : document[
"cells"].GetArray())
297 if (!cell.IsObject())
302 const std::string cell_name = cell.HasMember(
"name") && cell[
"name"].IsString() ? cell[
"name"].GetString() :
"(unknown)";
304 rename_legacy_types(cell, allocator);
305 if (cell.HasMember(
"lut_config"))
307 upgrade_lut_config(cell, cell_name, allocator, warnings);
309 add_parameters(cell, allocator);
318 std::filesystem::path backup_path = file_path;
319 backup_path.replace_extension(
".json");
328 if (std::filesystem::exists(backup_path, ec))
330 return ERR(
"could not upgrade HGL file '" + file_path.string() +
"': cannot keep the original as '" + backup_path.string() +
"', that file already exists");
333 std::filesystem::rename(file_path, backup_path, ec);
336 return ERR(
"could not upgrade HGL file '" + file_path.string() +
"': unable to rename it to '" + backup_path.string() +
"': " + ec.message());
339 FILE* fp = fopen(file_path.string().c_str(),
"w");
340 bool written =
false;
344 rapidjson::FileWriteStream os(fp, buffer,
sizeof(buffer));
345 rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(os);
346 written = document.Accept(writer);
348 written = (fclose(fp) == 0) && written;
355 std::filesystem::rename(backup_path, file_path, ec);
356 return ERR(
"could not upgrade HGL file '" + file_path.string() +
"': unable to write the upgraded file");
359 return OK(backup_path);
void remove(std::filesystem::path file_path)
std::filesystem::path get_backup_path(const std::filesystem::path &file_path)
Result< std::vector< std::string > > upgrade_document(rapidjson::Document &document)
Result< std::filesystem::path > replace_file(const rapidjson::Document &document, const std::filesystem::path &file_path)
std::error_code error_code
std::vector< PinInformation > pins