diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 73ffcbf3e..057c8b46b 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -456,21 +456,26 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) if (wire->attributes.count(ID::single_bit_vector)) range = stringf(" [%d:%d]", wire->start_offset, wire->start_offset); } + // Emit `signed` for wires/ports whose RTLIL is_signed flag is set. + // Without this, an `output signed [N:0] o` was silently demoted to plain + // `output [N:0] o` on write, losing the declared signedness in a + // read_verilog -> write_verilog round-trip. + const char *signed_kw = wire->is_signed ? " signed" : ""; if (wire->port_input && !wire->port_output) - f << stringf("%s" "input%s %s;\n", indent, range, id(wire->name)); + f << stringf("%s" "input%s%s %s;\n", indent, signed_kw, range, id(wire->name)); if (!wire->port_input && wire->port_output) - f << stringf("%s" "output%s %s;\n", indent, range, id(wire->name)); + f << stringf("%s" "output%s%s %s;\n", indent, signed_kw, range, id(wire->name)); if (wire->port_input && wire->port_output) - f << stringf("%s" "inout%s %s;\n", indent, range, id(wire->name)); + f << stringf("%s" "inout%s%s %s;\n", indent, signed_kw, range, id(wire->name)); if (reg_wires.count(wire->name)) { - f << stringf("%s" "reg%s %s", indent, range, id(wire->name)); + f << stringf("%s" "reg%s%s %s", indent, signed_kw, range, id(wire->name)); if (wire->attributes.count(ID::init)) { f << stringf(" = "); dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); } else - f << stringf("%s" "wire%s %s;\n", indent, range, id(wire->name)); + f << stringf("%s" "wire%s%s %s;\n", indent, signed_kw, range, id(wire->name)); #endif } diff --git a/tests/various/write_verilog_signed_port.ys b/tests/various/write_verilog_signed_port.ys new file mode 100644 index 000000000..1d534cbd9 --- /dev/null +++ b/tests/various/write_verilog_signed_port.ys @@ -0,0 +1,33 @@ +# Roundtrip a module with `signed` ports / wires through read_verilog +# and write_verilog and verify the `signed` keyword survives. Before +# the fix, write_verilog silently dropped `signed` on every wire / port +# declaration, even though wire->is_signed was tracked correctly +# through the RTLIL. The IEEE 1364-2001 grammar (Annex A.2.1.2 / +# A.2.1.3) allows `signed` after the direction / net-type, which is +# the dialect write_verilog targets by default. + +! mkdir -p temp +read_verilog <