Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions backends/verilog/verilog_backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
33 changes: 33 additions & 0 deletions tests/various/write_verilog_signed_port.ys
Original file line number Diff line number Diff line change
@@ -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 <<EOT
module top(
input signed [9:0] in,
input signed s_in,
output signed [31:0] o,
output signed s_o
);
wire signed [15:0] w;
assign w = in;
assign o = w;
assign s_o = s_in;
endmodule
EOT

write_verilog -noattr temp/write_verilog_signed_port.v

# Each `signed` declaration must round-trip exactly. Use a non-greedy
# grep on the relevant slices (input / output / wire) so any drift
# in the surrounding formatting doesn't mask the bug.
! grep -E '^\s*input *signed *\[9:0\] +in;' temp/write_verilog_signed_port.v
! grep -E '^\s*input *signed +s_in;' temp/write_verilog_signed_port.v
! grep -E '^\s*output *signed *\[31:0\] +o;' temp/write_verilog_signed_port.v
! grep -E '^\s*output *signed +s_o;' temp/write_verilog_signed_port.v
! grep -E '^\s*wire *signed *\[15:0\] +w;' temp/write_verilog_signed_port.v