diff --git a/backends/json/json.cc b/backends/json/json.cc index 749fe1fc3..cd45918d0 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -178,8 +178,10 @@ struct JsonWriter f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); - if (w->upto) + if (w->width != 1 && w->upto) f << stringf(" \"upto\": 1,\n"); + if (w->width == 1 && w->sbvector) + f << stringf(" \"sbvector\": 1,\n"); if (w->is_signed) f << stringf(" \"signed\": %d,\n", w->is_signed); f << stringf(" \"bits\": %s\n", get_bits(w).c_str()); @@ -270,8 +272,10 @@ struct JsonWriter f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); - if (w->upto) + if (w->width != 1 && w->upto) f << stringf(" \"upto\": 1,\n"); + if (w->width == 1 && w->sbvector) + f << stringf(" \"sbvector\": 1,\n"); if (w->is_signed) f << stringf(" \"signed\": %d,\n", w->is_signed); f << stringf(" \"attributes\": {"); @@ -403,10 +407,12 @@ struct JsonBackend : public Backend { log(" \"bits\": \n"); log(" \"offset\": \n"); log(" \"upto\": <1 if the port bit indexing is MSB-first>\n"); + log(" \"sbvector\": <1 if a single-bit port is a vector, not a scalar>\n"); log(" \"signed\": <1 if the port is signed>\n"); log(" }\n"); log("\n"); - log("The \"offset\" and \"upto\" fields are skipped if their value would be 0.\n"); + log("The \"offset\", \"upto\", and \"sbvector\" fields are skipped\n"); + log("if their value would be 0.\n"); log("They don't affect connection semantics, and are only used to preserve original\n"); log("HDL bit indexing.\n"); log("And is:\n"); @@ -453,6 +459,7 @@ struct JsonBackend : public Backend { log(" \"bits\": \n"); log(" \"offset\": \n"); log(" \"upto\": <1 if the port bit indexing is MSB-first>\n"); + log(" \"sbvector\": <1 if a single-bit port is a vector, not a scalar>\n"); log(" \"signed\": <1 if the port is signed>\n"); log(" }\n"); log("\n"); diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index ae60ee6c7..62fe7388e 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -130,10 +130,14 @@ void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL:: wire->driverCell()->name.c_str(), wire->driverPort().c_str()); } f << stringf("%s" "wire ", indent.c_str()); - if (wire->width != 1) + if (wire->width == 1) { + if (wire->sbvector) + f << stringf("width %d ", wire->width); + } else { f << stringf("width %d ", wire->width); - if (wire->upto) - f << stringf("upto "); + if (wire->upto) + f << stringf("upto "); + } if (wire->start_offset != 0) f << stringf("offset %d ", wire->start_offset); if (wire->port_input && !wire->port_output) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 2bc6ff3b8..e4ac4d175 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -419,6 +419,9 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset); else range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset); + } else { + if (wire->sbvector) + range = stringf(" [%d:%d]", wire->start_offset, wire->start_offset); } if (wire->port_input && !wire->port_output) f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 431f7b4f8..5f95de35f 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -350,6 +350,8 @@ void AstNode::dumpAst(FILE *f, std::string indent) const fprintf(f, " port=%d", port_id); if (range_valid || range_left != -1 || range_right != 0) fprintf(f, " %srange=[%d:%d]%s", range_swapped ? "swapped_" : "", range_left, range_right, range_valid ? "" : "!"); + if (is_sbvector) + fprintf(f, " vector"); if (integer != 0) fprintf(f, " int=%u", (int)integer); if (realvalue != 0) diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 2c2d408ce..5016d878d 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -192,7 +192,7 @@ namespace AST // node content - most of it is unused in most node types std::string str; std::vector bits; - bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized, is_custom_type; + bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, is_sbvector, was_checked, is_unsized, is_custom_type; int port_id, range_left, range_right; uint32_t integer; double realvalue; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d3982b92b..97451b623 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1445,7 +1445,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) wire->port_id = port_id; wire->port_input = is_input; wire->port_output = is_output; - wire->upto = range_swapped; + if (wire->width != 1) + wire->upto = range_swapped; + else + wire->sbvector = is_sbvector; + wire->is_signed = is_signed; for (auto &attr : attributes) { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 3411d6c03..bf176937b 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -2084,6 +2084,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::swap(range_left, range_right); range_swapped = force_upto; } + if (range_left == range_right) + is_sbvector = true; } } else { if (!range_valid) @@ -2092,6 +2094,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin range_swapped = false; range_left = 0; range_right = 0; + is_sbvector = false; } } diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 1aab81015..c859bbf47 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -345,6 +345,12 @@ void json_import(Design *design, string &modname, JsonNode *node) port_wire->upto = val->data_number != 0; } + if (port_node->data_dict.count("sbvector") != 0) { + JsonNode *val = port_node->data_dict.at("sbvector"); + if (val->type == 'N') + port_wire->sbvector = val->data_number != 0; + } + if (port_node->data_dict.count("signed") != 0) { JsonNode *val = port_node->data_dict.at("signed"); if (val->type == 'N') @@ -442,6 +448,11 @@ void json_import(Design *design, string &modname, JsonNode *node) if (val->type == 'N') wire->upto = val->data_number != 0; } + if (net_node->data_dict.count("sbvector") != 0) { + JsonNode *val = net_node->data_dict.at("sbvector"); + if (val->type == 'N') + wire->sbvector = val->data_number != 0; + } if (net_node->data_dict.count("offset") != 0) { JsonNode *val = net_node->data_dict.at("offset"); diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y index fc7615364..67c9dafbe 100644 --- a/frontends/rtlil/rtlil_parser.y +++ b/frontends/rtlil/rtlil_parser.y @@ -188,6 +188,9 @@ wire_stmt: wire_options: wire_options TOK_WIDTH TOK_INT { current_wire->width = $3; + // Width 1 specified -> single-bit vector rather than scalar + if (current_wire->width == 1) + current_wire->sbvector = true; } | wire_options TOK_WIDTH TOK_INVALID { rtlil_frontend_yyerror("RTLIL error: invalid wire width"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 96c8c523b..d1210714b 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1803,6 +1803,16 @@ namespace RTLIL_BACKEND { void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire); } +struct BoolStruct { +private: + bool val; +public: + BoolStruct(bool v) : val(v) {} + operator bool() const { + return val; + } +}; + struct RTLIL::Wire : public RTLIL::NamedObject { Hasher::hash_t hashidx_; @@ -1827,7 +1837,16 @@ public: RTLIL::Module *module; int width, start_offset, port_id; - bool port_input, port_output, upto, is_signed; + bool port_input, port_output, is_signed; + // These are actually just total aliases, relying on + // common initial sequences of records to avoid UB. + // This is a retrofit and we don't know if we ensure + // only the active member is accessed + union { + BoolStruct upto; // if width >= 1 + // "single bit vector" vs scalar + BoolStruct sbvector; // if width == 1 + }; RTLIL::Cell *driverCell() const { log_assert(driverCell_); return driverCell_; }; RTLIL::IdString driverPort() const { log_assert(driverCell_); return driverPort_; }; diff --git a/tests/various/json_sbvector.ys b/tests/various/json_sbvector.ys new file mode 100644 index 000000000..f9a74f0a5 --- /dev/null +++ b/tests/various/json_sbvector.ys @@ -0,0 +1,36 @@ +read_verilog <