|
12 | 12 | #include "netlist_test_utils.h" |
13 | 13 |
|
14 | 14 | #include <filesystem> |
| 15 | +#include <fstream> |
| 16 | +#include <sstream> |
15 | 17 |
|
16 | 18 | namespace hal { |
17 | 19 | using test_utils::MIN_GATE_ID; |
@@ -164,6 +166,34 @@ namespace hal { |
164 | 166 |
|
165 | 167 | return nl; |
166 | 168 | } |
| 169 | + |
| 170 | + // Write a minimal old-format .hal JSON to path. gate_data_entries and |
| 171 | + // mod_data_entries are raw JSON arrays for the "data" key (pass "" to omit). |
| 172 | + // gate_param_entries is a raw JSON object for the "parameters" key (pass "" to omit). |
| 173 | + void write_old_format_hal(const std::filesystem::path& path, |
| 174 | + const std::string& gate_data_entries, |
| 175 | + const std::string& gate_param_entries, |
| 176 | + const std::string& mod_data_entries) const |
| 177 | + { |
| 178 | + std::string gate_data_section = gate_data_entries.empty() ? "" : ",\"data\":" + gate_data_entries; |
| 179 | + std::string gate_param_section = gate_param_entries.empty() ? "" : ",\"parameters\":" + gate_param_entries; |
| 180 | + std::string mod_data_section = mod_data_entries.empty() ? "" : ",\"data\":" + mod_data_entries; |
| 181 | + |
| 182 | + std::ostringstream ss; |
| 183 | + ss << R"({"serialization_format_version":14,"netlist":{"gate_library":")" |
| 184 | + << m_gl->get_path().string() |
| 185 | + << R"(","id":1,"input_file":"","design_name":"","device_name":"",)" |
| 186 | + << R"("gates":[{"id":1,"name":"test_gate","type":"PARAM_TEST")" |
| 187 | + << gate_data_section << gate_param_section |
| 188 | + << R"(}],"global_vcc":[],"global_gnd":[],)" |
| 189 | + << R"("nets":[],"global_in":[],"global_out":[],)" |
| 190 | + << R"("modules":[{"id":1,"name":"top","parent":0,"type":"","gates":[1])" |
| 191 | + << mod_data_section |
| 192 | + << R"(}]}})"; |
| 193 | + |
| 194 | + std::ofstream f(path.string()); |
| 195 | + f << ss.str(); |
| 196 | + } |
167 | 197 | }; |
168 | 198 |
|
169 | 199 | // /** |
@@ -346,6 +376,149 @@ namespace hal { |
346 | 376 | TEST_END |
347 | 377 | } |
348 | 378 |
|
| 379 | + TEST_F(NetlistSerializerTest, check_generic_data_migration) |
| 380 | + { |
| 381 | + TEST_START |
| 382 | + { |
| 383 | + // Gate migration: gate type declarations take precedence. |
| 384 | + // Old-format "bit_vector" value "CAFE" (no prefix) → "0xCAFE", BitVector(16) from gate type. |
| 385 | + // Old-format "string" value for an Enum field → Enum declaration from gate type. |
| 386 | + // Non-"generic" data entries are left untouched. |
| 387 | + auto path = test_utils::create_sandbox_path("migration_gate.hal"); |
| 388 | + write_old_format_hal(path, |
| 389 | + R"([["generic","width","bit_vector","CAFE"],)" |
| 390 | + R"(["generic","mode","string","inverted"],)" |
| 391 | + R"(["attribute","info","string","keep_me"]])", |
| 392 | + "", ""); |
| 393 | + |
| 394 | + NO_COUT_TEST_BLOCK; |
| 395 | + auto nl = netlist_serializer::deserialize_from_file(path); |
| 396 | + ASSERT_NE(nl, nullptr); |
| 397 | + Gate* g = nl->get_gate_by_id(1); |
| 398 | + ASSERT_NE(g, nullptr); |
| 399 | + |
| 400 | + // "width": gate-type's BitVector(16) declaration used, unprefixed hex prepended |
| 401 | + EXPECT_TRUE(g->has_parameter("width")); |
| 402 | + EXPECT_EQ(g->get_parameter_value("width").get(), "0xCAFE"); |
| 403 | + EXPECT_EQ(g->get_parameter_declaration("width").get(), |
| 404 | + m_gl->get_gate_type_by_name("PARAM_TEST")->get_parameter("width").get()); |
| 405 | + |
| 406 | + // "mode": gate-type's Enum declaration used, value validated as enum member |
| 407 | + EXPECT_TRUE(g->has_parameter("mode")); |
| 408 | + EXPECT_EQ(g->get_parameter_value("mode").get(), "inverted"); |
| 409 | + EXPECT_EQ(g->get_parameter_declaration("mode").get(), |
| 410 | + m_gl->get_gate_type_by_name("PARAM_TEST")->get_parameter("mode").get()); |
| 411 | + |
| 412 | + // "generic" data entries are deleted after successful migration |
| 413 | + EXPECT_FALSE(g->has_data("generic", "width")); |
| 414 | + EXPECT_FALSE(g->has_data("generic", "mode")); |
| 415 | + |
| 416 | + // non-"generic" category data is not touched |
| 417 | + EXPECT_FALSE(g->has_parameter("info")); |
| 418 | + EXPECT_TRUE(g->has_data("attribute", "info")); |
| 419 | + } |
| 420 | + { |
| 421 | + // Module migration: inference from data-type string, covering all supported types. |
| 422 | + auto path = test_utils::create_sandbox_path("migration_module_types.hal"); |
| 423 | + write_old_format_hal(path, "", "", |
| 424 | + R"([["generic","B", "boolean", "true" ],)" |
| 425 | + R"( ["generic","I", "integer", "-3" ],)" |
| 426 | + R"( ["generic","F", "floating_point", "2.5" ],)" |
| 427 | + R"( ["generic","T", "time", "10ns" ],)" |
| 428 | + R"( ["generic","S", "string", "hello"],)" |
| 429 | + R"( ["generic","V0", "bit_value", "0" ],)" |
| 430 | + R"( ["generic","V1", "bit_value", "1" ],)" |
| 431 | + R"( ["generic","VX", "bit_value", "X" ],)" |
| 432 | + R"( ["generic","VZ", "std_logic", "Z" ],)" |
| 433 | + R"( ["generic","BV", "bit_vector", "ABCD" ],)" |
| 434 | + R"( ["generic","BP", "bit_vector", "0x12" ],)" |
| 435 | + R"( ["generic","LV", "std_logic_vector", "0bXX01"],)" |
| 436 | + R"( ["other", "O", "string", "skip" ]])"); |
| 437 | + |
| 438 | + NO_COUT_TEST_BLOCK; |
| 439 | + auto nl = netlist_serializer::deserialize_from_file(path); |
| 440 | + ASSERT_NE(nl, nullptr); |
| 441 | + Module* top = nl->get_top_module(); |
| 442 | + ASSERT_NE(top, nullptr); |
| 443 | + |
| 444 | + EXPECT_EQ(top->get_parameter_declaration("B").get().get_type(), Parameter::Type::Boolean); |
| 445 | + EXPECT_EQ(top->get_parameter_value("B").get(), "true"); |
| 446 | + |
| 447 | + EXPECT_EQ(top->get_parameter_declaration("I").get().get_type(), Parameter::Type::Integer); |
| 448 | + EXPECT_EQ(top->get_parameter_value("I").get(), "-3"); |
| 449 | + |
| 450 | + EXPECT_EQ(top->get_parameter_declaration("F").get().get_type(), Parameter::Type::Float); |
| 451 | + EXPECT_EQ(top->get_parameter_value("F").get(), "2.5"); |
| 452 | + |
| 453 | + EXPECT_EQ(top->get_parameter_declaration("T").get().get_type(), Parameter::Type::Time); |
| 454 | + EXPECT_EQ(top->get_parameter_value("T").get(), "10ns"); |
| 455 | + |
| 456 | + EXPECT_EQ(top->get_parameter_declaration("S").get().get_type(), Parameter::Type::String); |
| 457 | + EXPECT_EQ(top->get_parameter_value("S").get(), "hello"); |
| 458 | + |
| 459 | + // "bit_value" '0'/'1' → BitVector(1) with "0b" prefix |
| 460 | + EXPECT_EQ(top->get_parameter_declaration("V0").get().get_type(), Parameter::Type::BitVector); |
| 461 | + EXPECT_EQ(top->get_parameter_declaration("V0").get().get_size(), 1u); |
| 462 | + EXPECT_EQ(top->get_parameter_value("V0").get(), "0b0"); |
| 463 | + |
| 464 | + EXPECT_EQ(top->get_parameter_declaration("V1").get().get_type(), Parameter::Type::BitVector); |
| 465 | + EXPECT_EQ(top->get_parameter_value("V1").get(), "0b1"); |
| 466 | + |
| 467 | + // "bit_value" state char → LogicVector(1) |
| 468 | + EXPECT_EQ(top->get_parameter_declaration("VX").get().get_type(), Parameter::Type::LogicVector); |
| 469 | + EXPECT_EQ(top->get_parameter_declaration("VX").get().get_size(), 1u); |
| 470 | + EXPECT_EQ(top->get_parameter_value("VX").get(), "0bX"); |
| 471 | + |
| 472 | + // "std_logic" → LogicVector(1) |
| 473 | + EXPECT_EQ(top->get_parameter_declaration("VZ").get().get_type(), Parameter::Type::LogicVector); |
| 474 | + EXPECT_EQ(top->get_parameter_value("VZ").get(), "0bZ"); |
| 475 | + |
| 476 | + // "bit_vector" unprefixed hex "ABCD" → BitVector(16), value "0xABCD" |
| 477 | + EXPECT_EQ(top->get_parameter_declaration("BV").get().get_type(), Parameter::Type::BitVector); |
| 478 | + EXPECT_EQ(top->get_parameter_declaration("BV").get().get_size(), 16u); |
| 479 | + EXPECT_EQ(top->get_parameter_value("BV").get(), "0xABCD"); |
| 480 | + |
| 481 | + // "bit_vector" already-prefixed "0x12" → BitVector(8), value "0x12" |
| 482 | + EXPECT_EQ(top->get_parameter_declaration("BP").get().get_type(), Parameter::Type::BitVector); |
| 483 | + EXPECT_EQ(top->get_parameter_declaration("BP").get().get_size(), 8u); |
| 484 | + EXPECT_EQ(top->get_parameter_value("BP").get(), "0x12"); |
| 485 | + |
| 486 | + // "std_logic_vector" prefixed "0bXX01" → LogicVector(4), value "0bXX01" |
| 487 | + EXPECT_EQ(top->get_parameter_declaration("LV").get().get_type(), Parameter::Type::LogicVector); |
| 488 | + EXPECT_EQ(top->get_parameter_declaration("LV").get().get_size(), 4u); |
| 489 | + EXPECT_EQ(top->get_parameter_value("LV").get(), "0bXX01"); |
| 490 | + |
| 491 | + // non-"generic" category entry is untouched |
| 492 | + EXPECT_FALSE(top->has_parameter("O")); |
| 493 | + EXPECT_TRUE(top->has_data("other", "O")); |
| 494 | + |
| 495 | + // migrated entries removed from data map |
| 496 | + for (const auto& key : {"B", "I", "F", "T", "S", "V0", "V1", "VX", "VZ", "BV", "BP", "LV"}) |
| 497 | + EXPECT_FALSE(top->has_data("generic", key)); |
| 498 | + } |
| 499 | + { |
| 500 | + // When a "parameters" section is present, migration is skipped. |
| 501 | + // The "generic" data entry stays in the data map. |
| 502 | + auto path = test_utils::create_sandbox_path("migration_skip.hal"); |
| 503 | + write_old_format_hal(path, |
| 504 | + R"([["generic","width","bit_vector","DEAD"]])", |
| 505 | + R"({"width":{"type":"bit_vector","size":16,"default":"0xCAFE","value":"0xBEEF"}})", |
| 506 | + ""); |
| 507 | + |
| 508 | + NO_COUT_TEST_BLOCK; |
| 509 | + auto nl = netlist_serializer::deserialize_from_file(path); |
| 510 | + ASSERT_NE(nl, nullptr); |
| 511 | + Gate* g = nl->get_gate_by_id(1); |
| 512 | + ASSERT_NE(g, nullptr); |
| 513 | + |
| 514 | + // Explicit "parameters" value is loaded |
| 515 | + EXPECT_EQ(g->get_parameter_value("width").get(), "0xBEEF"); |
| 516 | + // Old "generic" data entry is left in the data map (migration was skipped) |
| 517 | + EXPECT_TRUE(g->has_data("generic", "width")); |
| 518 | + } |
| 519 | + TEST_END |
| 520 | + } |
| 521 | + |
349 | 522 | /** |
350 | 523 | * Testing the serialization and deserialization of a netlist with invalid input |
351 | 524 | * |
|
0 commit comments