Added support for $readmemh/$readmemb
authorClifford Wolf <clifford@clifford.at>
Sun, 26 Oct 2014 19:33:10 +0000 (20:33 +0100)
committerClifford Wolf <clifford@clifford.at>
Sun, 26 Oct 2014 19:33:10 +0000 (20:33 +0100)
README
frontends/ast/ast.h
frontends/ast/simplify.cc
kernel/rtlil.cc

diff --git a/README b/README
index 32a47cbfee1a93c677abeb4c6114b90c3b1a4505..1eb6f10aaf2bbcf9a23b5bc0917cc13b2d330280 100644 (file)
--- a/README
+++ b/README
@@ -384,7 +384,6 @@ Other Unsorted TODOs
 - Implement missing Verilog 2005 features:
 
   - Support for real (float) const. expressions and parameters
-  - ROM modeling using $readmemh/$readmemb in "initial" blocks
   - Ignore what needs to be ignored (e.g. drive and charge strengths)
   - Check standard vs. implementation to identify missing features
 
index 56f5d888dafe33179015c776aab6ed0262a013ab..02375538723089c5eed072d69e83d877bbffa368 100644 (file)
@@ -204,6 +204,7 @@ namespace AST
                // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
                // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
                bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
+               AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr);
                void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map);
                void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules);
                void mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places,
index e2d63de606325b94e00088eb9c1cb74f3b3e86eb..f16c3d067b29c76b91da7034b164f8c666ea1188 100644 (file)
 
 #include "kernel/log.h"
 #include "libs/sha1/sha1.h"
+#include "frontends/verilog/verilog_frontend.h"
 #include "ast.h"
 
 #include <sstream>
 #include <stdarg.h>
+#include <stdlib.h>
 #include <math.h>
 
 YOSYS_NAMESPACE_BEGIN
@@ -1480,6 +1482,44 @@ skip_dynamic_range_lvalue_expansion:;
                                log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
                }
                if (type == AST_TCALL) {
+                       if (str == "\\$readmemh" || str == "\\$readmemb")
+                       {
+                               if (GetSize(children) < 2 || GetSize(children) > 4)
+                                       log_error("System function %s got %d arguments, expected 2-4 at %s:%d.\n",
+                                                       RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+                               AstNode *node_filename = children[0]->clone();
+                               while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+                               if (node_filename->type != AST_CONSTANT)
+                                       log_error("Failed to evaluate system function `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+                               AstNode *node_memory = children[1]->clone();
+                               while (node_memory->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+                               if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY)
+                                       log_error("Failed to evaluate system function `%s' with non-memory 2nd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+                               int start_addr = -1, finish_addr = -1;
+
+                               if (GetSize(children) > 2) {
+                                       AstNode *node_addr = children[2]->clone();
+                                       while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+                                       if (node_addr->type != AST_CONSTANT)
+                                               log_error("Failed to evaluate system function `%s' with non-constant 3rd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+                                       start_addr = node_addr->asInt(false);
+                               }
+
+                               if (GetSize(children) > 3) {
+                                       AstNode *node_addr = children[3]->clone();
+                                       while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+                                       if (node_addr->type != AST_CONSTANT)
+                                               log_error("Failed to evaluate system function `%s' with non-constant 4th argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+                                       finish_addr = node_addr->asInt(false);
+                               }
+
+                               newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr);
+                               goto apply_newNode;
+                       }
+
                        if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK)
                                log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
                }
@@ -1988,6 +2028,78 @@ static void replace_result_wire_name_in_function(AstNode *node, std::string &fro
                node->str = to;
 }
 
+// replace a readmem[bh] TCALL ast node with a block of memory assignments
+AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr)
+{
+       AstNode *block = new AstNode(AST_BLOCK);
+
+       std::ifstream f;
+       f.open(mem_filename.c_str());
+
+       if (f.fail())
+               log_error("Can not open file `%s` for %s at %s:%d.\n", mem_filename.c_str(), str.c_str(), filename.c_str(), linenum);
+
+       // log_assert(GetSize(memory->children) == 2 && memory->children[0]->type == AST_RANGE && memory->children[0]->range_valid);
+       // int wordsize_left =  memory->children[0]->range_left, wordsize_right =  memory->children[0]->range_right;
+       // int wordsize = std::max(wordsize_left, wordsize_right) - std::min(wordsize_left, wordsize_right) + 1;
+
+       bool in_comment = false;
+       int increment = (start_addr < finish_addr) || (start_addr < 0) || (finish_addr < 0) ? +1 : -1;
+       int cursor = start_addr < 0 ? 0 : start_addr;
+
+       while (!f.eof())
+       {
+               std::string line, token;
+               std::getline(f, line);
+
+               for (int i = 0; i < GetSize(line); i++) {
+                       if (in_comment && line.substr(i, 2) == "*/") {
+                               line[i] = ' ';
+                               line[i+1] = ' ';
+                               in_comment = false;
+                               continue;
+                       }
+                       if (!in_comment && line.substr(i, 2) == "/*")
+                               in_comment = true;
+                       if (in_comment)
+                               line[i] = ' ';
+               }
+
+               while (1)
+               {
+                       token = next_token(line, " \t\r\n");
+                       if (token.empty() || token.substr(0, 2) == "//")
+                               break;
+
+                       if (token[0] == '@') {
+                               token = token.substr(1);
+                               const char *nptr = token.c_str();
+                               char *endptr;
+                               cursor = strtol(nptr, &endptr, 16);
+                               if (!*nptr || *endptr)
+                                       log_error("Can not parse address `%s` for %s at %s:%d.\n", nptr, str.c_str(), filename.c_str(), linenum);
+                               continue;
+                       }
+
+                       AstNode *value = VERILOG_FRONTEND::const2ast((is_readmemh ? "'h" : "'b") + token);
+
+                       block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value));
+                       block->children.back()->children[0]->str = memory->str;
+                       block->children.back()->children[0]->id2ast = memory;
+
+                       if (cursor == finish_addr)
+                               break;
+                       cursor += increment;
+               }
+
+               if (cursor == finish_addr)
+                       break;
+       }
+
+       // fixme
+       return block;
+}
+
 // annotate the names of all wires and other named objects in a generate block
 void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map)
 {
index 1a9eb4d14c0486b8a6d04343577c2d808f84cc79..8cfc0c5f981ef9930b3fe66b3e5ac0d9cdc0d3c0 100644 (file)
@@ -2935,7 +2935,7 @@ bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::stri
                if (netname.size() == 0)
                        continue;
 
-               if ('0' <= netname[0] && netname[0] <= '9') {
+               if (('0' <= netname[0] && netname[0] <= '9') || netname[0] == '\'') {
                        cover("kernel.rtlil.sigspec.parse.const");
                        AST::get_line_num = sigspec_parse_get_dummy_line_num;
                        AST::AstNode *ast = VERILOG_FRONTEND::const2ast(netname);