| /* | 
 |  * Copyright 2020-2022 F4PGA Authors | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  * | 
 |  */ | 
 |  | 
 | #include "kernel/sigtools.h" | 
 | #include "kernel/yosys.h" | 
 | #include <bitset> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 |  | 
 | USING_YOSYS_NAMESPACE | 
 | PRIVATE_NAMESPACE_BEGIN | 
 |  | 
 | static void run_pp3_braminit(Module *module) | 
 | { | 
 |     for (auto cell : module->selected_cells()) { | 
 |         uint32_t mem[2048]; | 
 |         int32_t ramDataWidth = 32; | 
 |         int32_t ramDataDepth = 512; | 
 |  | 
 |         log("cell type %s\n", RTLIL::id2cstr(cell->name)); | 
 |  | 
 |         /* Only consider cells we're interested in */ | 
 |         if (cell->type != ID(RAM_16K_BLK) && cell->type != ID(RAM_8K_BLK)) | 
 |             continue; | 
 |         log("found ram block\n"); | 
 |         if (!cell->hasParam(ID(INIT_FILE))) | 
 |             continue; | 
 |         std::string init_file = cell->getParam(ID(INIT_FILE)).decode_string(); | 
 |         cell->unsetParam(ID(INIT_FILE)); | 
 |         if (init_file == "") | 
 |             continue; | 
 |  | 
 |         /* Open file */ | 
 |         log("Processing %s : %s\n", RTLIL::id2cstr(cell->name), init_file.c_str()); | 
 |         ramDataWidth = cell->getParam(ID(data_width_int)).as_int(); | 
 |         ramDataDepth = cell->getParam(ID(data_depth_int)).as_int(); | 
 |  | 
 |         std::ifstream f; | 
 |         f.open(init_file.c_str()); | 
 |         if (f.fail()) { | 
 |             log("Can not open file `%s`.\n", init_file.c_str()); | 
 |             continue; | 
 |         } | 
 |  | 
 |         /* Defaults to 0 */ | 
 |         memset(mem, 0x00, sizeof(mem)); | 
 |  | 
 |         /* Process each line */ | 
 |         bool in_comment = false; | 
 |         int cursor = 0; | 
 |  | 
 |         while (!f.eof()) { | 
 |             std::string line, token; | 
 |             std::getline(f, line); | 
 |  | 
 |             for (int i = 0; i < GetSize(line); i++) { | 
 |                 if (in_comment && line.compare(i, 2, "*/") == 0) { | 
 |                     line[i] = ' '; | 
 |                     line[i + 1] = ' '; | 
 |                     in_comment = false; | 
 |                     continue; | 
 |                 } | 
 |                 if (!in_comment && line.compare(i, 2, "/*") == 0) | 
 |                     in_comment = true; | 
 |                 if (in_comment) | 
 |                     line[i] = ' '; | 
 |             } | 
 |  | 
 |             while (1) { | 
 |                 bool set_cursor = false; | 
 |                 long value; | 
 |  | 
 |                 token = next_token(line, " \t\r\n"); | 
 |                 if (token.empty() || token.compare(0, 2, "//") == 0) | 
 |                     break; | 
 |  | 
 |                 if (token[0] == '@') { | 
 |                     token = token.substr(1); | 
 |                     set_cursor = true; | 
 |                 } | 
 |  | 
 |                 const char *nptr = token.c_str(); | 
 |                 char *endptr; | 
 |                 value = strtol(nptr, &endptr, 16); | 
 |                 if (!*nptr || *endptr) { | 
 |                     log("Can not parse %s `%s` for %s.\n", set_cursor ? "address" : "value", nptr, token.c_str()); | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 if (set_cursor) | 
 |                     cursor = value; | 
 |                 else if (cursor >= 0 && cursor < ramDataDepth) | 
 |                     mem[cursor++] = value; | 
 |                 else | 
 |                     log("Attempt to initialize non existent address %d\n", cursor); | 
 |             } | 
 |         } | 
 |  | 
 |         // TODO: Support RAM initialization for other widths than 8, 16 and 32 | 
 |         if (ramDataWidth != 8 && ramDataWidth != 16 && ramDataWidth != 32) { | 
 |             log("WARNING: The RAM cell '%s' has data width of %d. Initialization of this width from a file is not supported yet!\n", | 
 |                 RTLIL::id2cstr(cell->name), ramDataWidth); | 
 |             continue; | 
 |         } | 
 |  | 
 |         /* Set attributes */ | 
 |         std::string val = ""; | 
 |         for (int i = ramDataDepth - 1; i >= 0; i--) { | 
 |             if (ramDataWidth == 8) | 
 |                 val += std::bitset<8>(mem[i]).to_string(); | 
 |             else if (ramDataWidth == 16) | 
 |                 val += std::bitset<16>(mem[i]).to_string(); | 
 |             else if (ramDataWidth == 32) | 
 |                 val += std::bitset<32>(mem[i]).to_string(); | 
 |         } | 
 |         cell->setParam(RTLIL::escape_id("INIT"), RTLIL::Const::from_string(val)); | 
 |     } | 
 | } | 
 |  | 
 | struct PP3BRAMInitPass : public Pass { | 
 |     PP3BRAMInitPass() : Pass("pp3_braminit", "PP3: perform RAM Block initialization from file") {} | 
 |     void help() override | 
 |     { | 
 |         //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | 
 |         log("\n"); | 
 |         log("    pp3_braminit\n"); | 
 |         log("\n"); | 
 |         log("This command processes all PP3 RAM blocks with a non-empty INIT_FILE\n"); | 
 |         log("parameter and converts it into the required INIT attributes\n"); | 
 |         log("\n"); | 
 |     } | 
 |     void execute(std::vector<std::string> args, RTLIL::Design *design) override | 
 |     { | 
 |         log_header(design, "Executing PP3_BRAMINIT pass.\n"); | 
 |  | 
 |         extra_args(args, 1, design); | 
 |  | 
 |         for (auto module : design->selected_modules()) | 
 |             run_pp3_braminit(module); | 
 |     } | 
 | } PP3BRAMInitPass; | 
 |  | 
 | PRIVATE_NAMESPACE_END |