HAL  v4.5.0-232-g8053ac4b3
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
upgrade_hgl.cpp
Go to the documentation of this file.
2 
3 #include "rapidjson/filewritestream.h"
4 #include "rapidjson/prettywriter.h"
5 
6 #include <algorithm>
7 #include <cstdio>
8 #include <map>
9 #include <system_error>
10 #include <utility>
11 
12 namespace hal
13 {
14  namespace hgl_upgrade
15  {
16  namespace
17  {
18  const std::map<std::string, std::string> LEGACY_TYPE_RENAMES = {{"buffer", "c_buffer"}, {"carry", "c_carry"}, {"mux", "c_mux"}, {"lut", "c_lut"}};
19 
21  using ParameterSizes = std::vector<std::pair<std::string, u64>>;
22 
23  const rapidjson::Value* find_member(const rapidjson::Value& object, const char* name)
24  {
25  if (!object.IsObject())
26  {
27  return nullptr;
28  }
29  auto it = object.FindMember(name);
30  return (it != object.MemberEnd()) ? &it->value : nullptr;
31  }
32 
33  std::string get_string(const rapidjson::Value& object, const char* name)
34  {
35  const rapidjson::Value* value = find_member(object, name);
36  return (value != nullptr && value->IsString()) ? value->GetString() : "";
37  }
38 
39  u64 get_uint(const rapidjson::Value& object, const char* name)
40  {
41  const rapidjson::Value* value = find_member(object, name);
42  return (value != nullptr && value->IsUint64()) ? value->GetUint64() : 0;
43  }
44 
45  rapidjson::Value make_string(const std::string& str, rapidjson::Document::AllocatorType& allocator)
46  {
47  return rapidjson::Value(str.c_str(), str.length(), allocator);
48  }
49 
50  void collect_pins(const rapidjson::Value& cell, std::vector<std::string>& input_pins, std::vector<std::string>& lut_output_pins)
51  {
52  const rapidjson::Value* pin_groups = find_member(cell, "pin_groups");
53  if (pin_groups == nullptr || !pin_groups->IsArray())
54  {
55  return;
56  }
57 
58  for (const auto& pin_group : pin_groups->GetArray())
59  {
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())
63  {
64  continue;
65  }
66 
67  for (const auto& pin : pins->GetArray())
68  {
69  if (direction == "input")
70  {
71  input_pins.push_back(get_string(pin, "name"));
72  }
73  else if (direction == "output" && get_string(pin, "type") == "lut")
74  {
75  lut_output_pins.push_back(get_string(pin, "name"));
76  }
77  }
78  }
79  }
80 
81  void rename_legacy_types(rapidjson::Value& cell, rapidjson::Document::AllocatorType& allocator)
82  {
83  if (!cell.HasMember("types") || !cell["types"].IsArray())
84  {
85  return;
86  }
87 
88  for (auto& type : cell["types"].GetArray())
89  {
90  if (!type.IsString())
91  {
92  continue;
93  }
94  if (auto it = LEGACY_TYPE_RENAMES.find(type.GetString()); it != LEGACY_TYPE_RENAMES.end())
95  {
96  type = make_string(it->second, allocator);
97  }
98  }
99  }
100 
101  void upgrade_lut_config(rapidjson::Value& cell, const std::string& cell_name, rapidjson::Document::AllocatorType& allocator, std::vector<std::string>& warnings)
102  {
103  rapidjson::Value& lut_config = cell["lut_config"];
104  if (!lut_config.IsObject() || lut_config.HasMember("output_pins") || !lut_config.HasMember("data_identifier"))
105  {
106  // already in the new format or nothing to upgrade
107  return;
108  }
109 
110  std::vector<std::string> input_pins;
111  std::vector<std::string> lut_output_pins;
112  collect_pins(cell, input_pins, lut_output_pins);
113 
114  if (lut_output_pins.empty())
115  {
116  warnings.push_back("cell '" + cell_name + "': no output pin with type 'lut' found, LUT configuration not migrated, please update manually.");
117  return;
118  }
119 
120  if (input_pins.empty())
121  {
122  warnings.push_back("cell '" + cell_name + "': no input pins found, LUT configuration not migrated, please update manually.");
123  return;
124  }
125 
126  if (lut_output_pins.size() > 1)
127  {
128  // the input list and the INIT bit range of each output cannot be derived from the pins alone
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.");
131  return;
132  }
133 
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");
136 
137  rapidjson::Value inputs(rapidjson::kArrayType);
138  for (const auto& pin : input_pins)
139  {
140  inputs.PushBack(make_string(pin, allocator), allocator);
141  }
142 
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);
150 
151  rapidjson::Value output_pins(rapidjson::kArrayType);
152  output_pins.PushBack(output_pin, allocator);
153 
154  rapidjson::Value new_lut_config(rapidjson::kObjectType);
155  if (bit_order != nullptr)
156  {
157  new_lut_config.AddMember("bit_order", rapidjson::Value(*bit_order, allocator), allocator);
158  }
159  if (const rapidjson::Value* data_category = find_member(lut_config, "data_category"); data_category != nullptr)
160  {
161  new_lut_config.AddMember("data_category", rapidjson::Value(*data_category, allocator), allocator);
162  }
163  new_lut_config.AddMember("output_pins", output_pins, allocator);
164 
165  // assigning keeps the position of 'lut_config' within the cell
166  lut_config = new_lut_config;
167  }
168 
169  u64* find_size(ParameterSizes& sizes, const std::string& identifier)
170  {
171  for (auto& [ident, size] : sizes)
172  {
173  if (ident == identifier)
174  {
175  return &size;
176  }
177  }
178  return nullptr;
179  }
180 
181  void set_size(ParameterSizes& sizes, const std::string& identifier, u64 size)
182  {
183  if (u64* existing = find_size(sizes, identifier); existing != nullptr)
184  {
185  *existing = size;
186  }
187  else
188  {
189  sizes.emplace_back(identifier, size);
190  }
191  }
192 
193  void add_parameters(rapidjson::Value& cell, rapidjson::Document::AllocatorType& allocator)
194  {
195  if (cell.HasMember("parameters"))
196  {
197  return;
198  }
199 
200  ParameterSizes sizes;
201 
202  if (const rapidjson::Value* lut_config = find_member(cell, "lut_config"); lut_config != nullptr)
203  {
204  if (const rapidjson::Value* output_pins = find_member(*lut_config, "output_pins"); output_pins != nullptr && output_pins->IsArray())
205  {
206  for (const auto& output_pin : output_pins->GetArray())
207  {
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");
210  const u64* existing = find_size(sizes, identifier);
211  set_size(sizes, identifier, (existing != nullptr) ? std::max(*existing, required) : required);
212  }
213  }
214  }
215 
216  // flip-flops and latches hold a single bit per identifier
217  for (const char* config_name : {"ff_config", "latch_config"})
218  {
219  if (const rapidjson::Value* config = find_member(cell, config_name); config != nullptr && config->HasMember("data_identifier"))
220  {
221  const std::string identifier = get_string(*config, "data_identifier");
222  if (find_size(sizes, identifier) == nullptr)
223  {
224  set_size(sizes, identifier, 1);
225  }
226  }
227  }
228 
229  if (const rapidjson::Value* ram_config = find_member(cell, "ram_config"); ram_config != nullptr)
230  {
231  const rapidjson::Value* identifiers = find_member(*ram_config, "data_identifiers");
232  if (identifiers != nullptr && identifiers->IsArray() && !identifiers->Empty() && ram_config->HasMember("bit_size"))
233  {
234  const u64 size = get_uint(*ram_config, "bit_size") / identifiers->Size();
235  for (const auto& identifier : identifiers->GetArray())
236  {
237  set_size(sizes, identifier.IsString() ? identifier.GetString() : "", size);
238  }
239  }
240  }
241 
242  if (sizes.empty())
243  {
244  return;
245  }
246 
247  rapidjson::Value parameters(rapidjson::kArrayType);
248  for (const auto& [identifier, size] : sizes)
249  {
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);
256  }
257  cell.AddMember("parameters", parameters, allocator);
258  }
259  } // namespace
260 
261  Result<std::vector<std::string>> upgrade_document(rapidjson::Document& document)
262  {
263  if (!document.IsObject())
264  {
265  return ERR("could not upgrade HGL document: document is not a JSON object");
266  }
267 
268  std::vector<std::string> warnings;
269 
270  u32 version = 1;
271  if (document.HasMember("version"))
272  {
273  if (!document["version"].IsUint())
274  {
275  return ERR("could not upgrade HGL document: invalid version entry");
276  }
277  version = document["version"].GetUint();
278  }
279 
280  if (version >= TARGET_VERSION)
281  {
282  return OK(warnings);
283  }
284 
285  if (version < 2)
286  {
287  return ERR("could not upgrade HGL document: version " + std::to_string(version) + " uses a legacy pin format that cannot be upgraded automatically");
288  }
289 
290  rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
291  document["version"].SetUint(TARGET_VERSION);
292 
293  if (document.HasMember("cells") && document["cells"].IsArray())
294  {
295  for (auto& cell : document["cells"].GetArray())
296  {
297  if (!cell.IsObject())
298  {
299  continue;
300  }
301 
302  const std::string cell_name = cell.HasMember("name") && cell["name"].IsString() ? cell["name"].GetString() : "(unknown)";
303 
304  rename_legacy_types(cell, allocator);
305  if (cell.HasMember("lut_config"))
306  {
307  upgrade_lut_config(cell, cell_name, allocator, warnings);
308  }
309  add_parameters(cell, allocator);
310  }
311  }
312 
313  return OK(warnings);
314  }
315 
316  std::filesystem::path get_backup_path(const std::filesystem::path& file_path)
317  {
318  std::filesystem::path backup_path = file_path;
319  backup_path.replace_extension(".json");
320  return backup_path;
321  }
322 
323  Result<std::filesystem::path> replace_file(const rapidjson::Document& document, const std::filesystem::path& file_path)
324  {
325  const std::filesystem::path backup_path = get_backup_path(file_path);
326 
327  std::error_code ec;
328  if (std::filesystem::exists(backup_path, ec))
329  {
330  return ERR("could not upgrade HGL file '" + file_path.string() + "': cannot keep the original as '" + backup_path.string() + "', that file already exists");
331  }
332 
333  std::filesystem::rename(file_path, backup_path, ec);
334  if (ec)
335  {
336  return ERR("could not upgrade HGL file '" + file_path.string() + "': unable to rename it to '" + backup_path.string() + "': " + ec.message());
337  }
338 
339  FILE* fp = fopen(file_path.string().c_str(), "w");
340  bool written = false;
341  if (fp != NULL)
342  {
343  char buffer[65536];
344  rapidjson::FileWriteStream os(fp, buffer, sizeof(buffer));
345  rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(os);
346  written = document.Accept(writer);
347  os.Flush();
348  written = (fclose(fp) == 0) && written;
349  }
350 
351  if (!written)
352  {
353  // put the original file back, so that nothing changed for the user
354  std::filesystem::remove(file_path, ec);
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");
357  }
358 
359  return OK(backup_path);
360  }
361  } // namespace hgl_upgrade
362 } // namespace hal
u32 size
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
void remove(std::filesystem::path file_path)
std::filesystem::path get_backup_path(const std::filesystem::path &file_path)
const u32 TARGET_VERSION
Definition: upgrade_hgl.h:58
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)
Definition: defines.h:45
std::error_code error_code
Definition: defines.h:46
PinType type
std::vector< PinInformation > pins
PinDirection direction
std::string name
std::string identifier