mirror of
https://github.com/YosysHQ/yosys
synced 2026-01-17 07:56:26 +00:00
splitnets: add -ports_only and -top_only options
Add two new options to the splitnets pass: - `-ports_only`: Split only module ports, not internal signals. This is useful when you want to split ports for interface compatibility while keeping internal signals as multi-bit wires for better readability. - `-top_only`: Apply splitting only at the top module level, not in submodules. This is helpful for hierarchical designs where you need split signals only at the top-level interface. These options can be combined with existing flags: - `splitnets -ports_only`: Split all ports in all modules - `splitnets -ports_only -top_only`: Split ports only in top module - `splitnets -ports -top_only`: Split both ports and nets only in top Add comprehensive tests that verify wire/port counts for all flag combinations using a hierarchical design.
This commit is contained in:
parent
71feb2a2a1
commit
6503ad91c9
4 changed files with 107 additions and 12 deletions
|
|
@ -113,6 +113,12 @@ struct SplitnetsPass : public Pass {
|
|||
log(" -ports\n");
|
||||
log(" also split module ports. per default only internal signals are split.\n");
|
||||
log("\n");
|
||||
log(" -ports_only\n");
|
||||
log(" split module ports, but not the internal signals.\n");
|
||||
log("\n");
|
||||
log(" -top_only\n");
|
||||
log(" split module ports/nets, only at the top level of hierarchy.\n");
|
||||
log("\n");
|
||||
log(" -driver\n");
|
||||
log(" don't blindly split nets in individual bits. instead look at the driver\n");
|
||||
log(" and split nets so that no driver drives only part of a net.\n");
|
||||
|
|
@ -122,6 +128,8 @@ struct SplitnetsPass : public Pass {
|
|||
{
|
||||
bool flag_ports = false;
|
||||
bool flag_driver = false;
|
||||
bool flag_ports_only = false;
|
||||
bool flag_top_only = false;
|
||||
std::string format = "[]:";
|
||||
|
||||
log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n");
|
||||
|
|
@ -137,6 +145,14 @@ struct SplitnetsPass : public Pass {
|
|||
flag_ports = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-ports_only") {
|
||||
flag_ports_only = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-top_only") {
|
||||
flag_top_only = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-driver") {
|
||||
flag_driver = true;
|
||||
continue;
|
||||
|
|
@ -152,11 +168,12 @@ struct SplitnetsPass : public Pass {
|
|||
{
|
||||
if (module->has_processes_warn())
|
||||
continue;
|
||||
|
||||
if (flag_top_only && (design->top_module() != module)) {
|
||||
continue;
|
||||
}
|
||||
SplitnetsWorker worker;
|
||||
|
||||
if (flag_ports)
|
||||
{
|
||||
if (flag_ports || flag_ports_only) {
|
||||
int normalized_port_factor = 0;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
|
|
@ -187,7 +204,8 @@ struct SplitnetsPass : public Pass {
|
|||
for (auto &chunk : sig.chunks()) {
|
||||
if (chunk.wire == NULL)
|
||||
continue;
|
||||
if (chunk.wire->port_id == 0 || flag_ports) {
|
||||
if ((flag_ports_only && (chunk.wire->port_id != 0)) ||
|
||||
(!flag_ports_only && (chunk.wire->port_id == 0 || flag_ports))) {
|
||||
if (chunk.offset != 0)
|
||||
split_wires_at[chunk.wire].insert(chunk.offset);
|
||||
if (chunk.offset + chunk.width < chunk.wire->width)
|
||||
|
|
@ -204,11 +222,12 @@ struct SplitnetsPass : public Pass {
|
|||
}
|
||||
worker.append_wire(module, it.first, cursor, it.first->width - cursor, format);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
for (auto wire : module->wires()) {
|
||||
if (((wire->width > 1) || (wire->has_attribute(ID::single_bit_vector)))
|
||||
if (flag_ports_only) {
|
||||
if (wire->width > 1 && (wire->port_id != 0) && design->selected(module, wire))
|
||||
worker.splitmap[wire] = std::vector<RTLIL::SigBit>();
|
||||
} else if (((wire->width > 1) || (wire->has_attribute(ID::single_bit_vector)))
|
||||
&& (wire->port_id == 0 || flag_ports)
|
||||
&& design->selected(module, wire)) {
|
||||
wire->attributes.erase(ID::single_bit_vector);
|
||||
|
|
@ -223,9 +242,8 @@ struct SplitnetsPass : public Pass {
|
|||
|
||||
module->rewrite_sigspecs(worker);
|
||||
|
||||
if (flag_ports)
|
||||
{
|
||||
for (auto wire : module->wires())
|
||||
if (flag_ports || flag_ports_only) {
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (wire->port_id == 0)
|
||||
continue;
|
||||
|
|
@ -248,7 +266,7 @@ struct SplitnetsPass : public Pass {
|
|||
delete_wires.insert(it.first);
|
||||
module->remove(delete_wires);
|
||||
|
||||
if (flag_ports)
|
||||
if (flag_ports || flag_ports_only)
|
||||
module->fixup_ports();
|
||||
}
|
||||
|
||||
|
|
|
|||
63
tests/various/test_splitnets.tcl
Normal file
63
tests/various/test_splitnets.tcl
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
yosys -import
|
||||
|
||||
proc read_stats { file } {
|
||||
set fid [open $file]
|
||||
set result [read $fid]
|
||||
close $fid
|
||||
set ports 0
|
||||
set nets 0
|
||||
foreach line [split $result "\n"] {
|
||||
if [regexp {Number of wires:[ \t]+([0-9]+)} $line tmp n] {
|
||||
set nets [expr $nets + $n]
|
||||
}
|
||||
if [regexp {Number of ports:[ \t]+([0-9]+)} $line tmp n] {
|
||||
set ports [expr $ports + $n]
|
||||
}
|
||||
}
|
||||
return [list $nets $ports]
|
||||
}
|
||||
|
||||
proc assert_count { type actual expected } {
|
||||
if {$actual != $expected} {
|
||||
puts "Error, $type count: $actual vs $expected expected"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
read_verilog test_splitnets.v
|
||||
hierarchy -auto-top
|
||||
procs
|
||||
design -save "pre"
|
||||
|
||||
splitnets -ports_only -top_only
|
||||
write_verilog -noexpr "ports_only_in_top.v"
|
||||
tee -o "ports_only_in_top.txt" stat
|
||||
foreach {nets ports} [read_stats "ports_only_in_top.txt"] {}
|
||||
assert_count "nets" $nets 26
|
||||
assert_count "ports" $ports 16
|
||||
|
||||
design -load "pre"
|
||||
splitnets -ports_only
|
||||
write_verilog -noexpr "ports_only_in_all.v"
|
||||
tee -o "ports_only_in_all.txt" stat
|
||||
foreach {nets ports} [read_stats "ports_only_in_all.txt"] {}
|
||||
assert_count "nets" $nets 30
|
||||
assert_count "ports" $ports 20
|
||||
|
||||
design -load "pre"
|
||||
splitnets -ports -top_only
|
||||
write_verilog -noexpr "ports_nets_in_top.v"
|
||||
tee -o "ports_nets_in_top.txt" stat
|
||||
foreach {nets ports} [read_stats "ports_nets_in_top.txt"] {}
|
||||
assert_count "nets" $nets 30
|
||||
assert_count "ports" $ports 16
|
||||
|
||||
design -load "pre"
|
||||
splitnets -ports
|
||||
write_verilog -noexpr "ports_nets_in_all.v"
|
||||
tee -o "ports_nets_in_all.txt" stat
|
||||
foreach {nets ports} [read_stats "ports_nets_in_all.txt"] {}
|
||||
assert_count "nets" $nets 40
|
||||
assert_count "ports" $ports 20
|
||||
|
||||
exit 0
|
||||
13
tests/various/test_splitnets.v
Normal file
13
tests/various/test_splitnets.v
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module bottom(input clk, input wire [1:0] i, output reg [1:0] q);
|
||||
reg [1:0] q1;
|
||||
always @(posedge clk) begin
|
||||
q1 <= i;
|
||||
q <= q1;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module top(input clk, input wire [1:0] i, output wire [1:0] q);
|
||||
wire [1:0] q1;
|
||||
bottom u1 (.clk(clk), .i(i), .q(q1));
|
||||
assign q = ~q1;
|
||||
endmodule
|
||||
1
tests/various/test_splitnets.ys
Normal file
1
tests/various/test_splitnets.ys
Normal file
|
|
@ -0,0 +1 @@
|
|||
tcl test_splitnets.tcl
|
||||
Loading…
Add table
Add a link
Reference in a new issue