497 lines
14 KiB
C++
497 lines
14 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2024, Parallax Software, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#include "VerilogWriter.hh"
|
|
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
|
|
#include "Error.hh"
|
|
#include "Liberty.hh"
|
|
#include "PortDirection.hh"
|
|
#include "Network.hh"
|
|
#include "NetworkCmp.hh"
|
|
#include "VerilogNamespace.hh"
|
|
#include "ParseBus.hh"
|
|
|
|
namespace sta {
|
|
|
|
using std::min;
|
|
using std::max;
|
|
|
|
class VerilogWriter
|
|
{
|
|
public:
|
|
VerilogWriter(const char *filename,
|
|
bool sort,
|
|
bool include_pwr_gnd,
|
|
CellSeq *remove_cells,
|
|
FILE *stream,
|
|
Network *network);
|
|
void writeModule(Instance *inst);
|
|
|
|
protected:
|
|
void writePorts(Cell *cell);
|
|
void writePortDcls(Cell *cell);
|
|
void writeWireDcls(Instance *inst);
|
|
const char *verilogPortDir(PortDirection *dir);
|
|
void writeChildren(Instance *inst);
|
|
void writeChild(Instance *child);
|
|
void writeInstPin(Instance *inst,
|
|
Port *port,
|
|
bool &first_port);
|
|
void writeInstBusPin(Instance *inst,
|
|
Port *port,
|
|
bool &first_port);
|
|
void writeInstBusPinBit(Instance *inst,
|
|
Port *port,
|
|
bool &first_member);
|
|
void writeAssigns(Instance *inst);
|
|
|
|
int findUnconnectedNetCount();
|
|
int findNCcount(Instance *inst);
|
|
int findChildNCcount(Instance *child);
|
|
int findPortNCcount(Instance *inst,
|
|
Port *port);
|
|
|
|
const char *filename_;
|
|
bool sort_;
|
|
bool include_pwr_gnd_;
|
|
CellSet remove_cells_;
|
|
FILE *stream_;
|
|
Network *network_;
|
|
|
|
CellSet written_cells_;
|
|
Vector<Instance*> pending_children_;
|
|
int unconnected_net_index_;
|
|
};
|
|
|
|
void
|
|
writeVerilog(const char *filename,
|
|
bool sort,
|
|
bool include_pwr_gnd,
|
|
CellSeq *remove_cells,
|
|
Network *network)
|
|
{
|
|
if (network->topInstance()) {
|
|
FILE *stream = fopen(filename, "w");
|
|
if (stream) {
|
|
VerilogWriter writer(filename, sort, include_pwr_gnd,
|
|
remove_cells, stream, network);
|
|
writer.writeModule(network->topInstance());
|
|
fclose(stream);
|
|
}
|
|
else
|
|
throw FileNotWritable(filename);
|
|
}
|
|
}
|
|
|
|
VerilogWriter::VerilogWriter(const char *filename,
|
|
bool sort,
|
|
bool include_pwr_gnd,
|
|
CellSeq *remove_cells,
|
|
FILE *stream,
|
|
Network *network) :
|
|
filename_(filename),
|
|
sort_(sort),
|
|
include_pwr_gnd_(include_pwr_gnd),
|
|
remove_cells_(network),
|
|
stream_(stream),
|
|
network_(network),
|
|
written_cells_(network),
|
|
unconnected_net_index_(1)
|
|
{
|
|
if (remove_cells) {
|
|
for(Cell *lib_cell : *remove_cells)
|
|
remove_cells_.insert(lib_cell);
|
|
}
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writeModule(Instance *inst)
|
|
{
|
|
Cell *cell = network_->cell(inst);
|
|
fprintf(stream_, "module %s (",
|
|
network_->name(cell));
|
|
writePorts(cell);
|
|
writePortDcls(cell);
|
|
fprintf(stream_, "\n");
|
|
writeWireDcls(inst);
|
|
fprintf(stream_, "\n");
|
|
writeChildren(inst);
|
|
writeAssigns(inst);
|
|
fprintf(stream_, "endmodule\n");
|
|
written_cells_.insert(cell);
|
|
|
|
if (sort_)
|
|
sort(pending_children_, [this](const Instance *inst1,
|
|
const Instance *inst2) {
|
|
return stringLess(network_->cellName(inst1), network_->cellName(inst2));
|
|
});
|
|
for (auto child : pending_children_) {
|
|
Cell *child_cell = network_->cell(child);
|
|
if (!written_cells_.hasKey(child_cell))
|
|
writeModule(child);
|
|
}
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writePorts(Cell *cell)
|
|
{
|
|
bool first = true;
|
|
CellPortIterator *port_iter = network_->portIterator(cell);
|
|
while (port_iter->hasNext()) {
|
|
Port *port = port_iter->next();
|
|
if (include_pwr_gnd_
|
|
|| !network_->direction(port)->isPowerGround()) {
|
|
if (!first)
|
|
fprintf(stream_, ",\n ");
|
|
string verillg_name = portVerilogName(network_->name(port),
|
|
network_->pathEscape());
|
|
fprintf(stream_, "%s", verillg_name.c_str());
|
|
first = false;
|
|
}
|
|
}
|
|
delete port_iter;
|
|
fprintf(stream_, ");\n");
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writePortDcls(Cell *cell)
|
|
{
|
|
CellPortIterator *port_iter = network_->portIterator(cell);
|
|
while (port_iter->hasNext()) {
|
|
Port *port = port_iter->next();
|
|
PortDirection *dir = network_->direction(port);
|
|
if (include_pwr_gnd_
|
|
|| !network_->direction(port)->isPowerGround()) {
|
|
string port_vname = portVerilogName(network_->name(port),
|
|
network_->pathEscape());
|
|
const char *vtype = verilogPortDir(dir);
|
|
if (vtype) {
|
|
fprintf(stream_, " %s", vtype);
|
|
if (network_->isBus(port))
|
|
fprintf(stream_, " [%d:%d]",
|
|
network_->fromIndex(port),
|
|
network_->toIndex(port));
|
|
fprintf(stream_, " %s;\n", port_vname.c_str());
|
|
if (dir->isTristate()) {
|
|
fprintf(stream_, " tri");
|
|
if (network_->isBus(port))
|
|
fprintf(stream_, " [%d:%d]",
|
|
network_->fromIndex(port),
|
|
network_->toIndex(port));
|
|
fprintf(stream_, " %s;\n", port_vname.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete port_iter;
|
|
}
|
|
|
|
const char *
|
|
VerilogWriter::verilogPortDir(PortDirection *dir)
|
|
{
|
|
if (dir == PortDirection::input())
|
|
return "input";
|
|
else if (dir == PortDirection::output())
|
|
return "output";
|
|
else if (dir == PortDirection::tristate())
|
|
return "output";
|
|
else if (dir == PortDirection::bidirect())
|
|
return "inout";
|
|
else if (dir == PortDirection::power())
|
|
return "inout";
|
|
else if (dir == PortDirection::ground())
|
|
return "inout";
|
|
else if (dir == PortDirection::internal())
|
|
return nullptr;
|
|
else {
|
|
criticalError(268, "unknown port direction");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
typedef std::pair<int, int> BusIndexRange;
|
|
|
|
void
|
|
VerilogWriter::writeWireDcls(Instance *inst)
|
|
{
|
|
Cell *cell = network_->cell(inst);
|
|
char escape = network_->pathEscape();
|
|
Map<string, BusIndexRange, std::less<string>> bus_ranges;
|
|
NetIterator *net_iter = network_->netIterator(inst);
|
|
while (net_iter->hasNext()) {
|
|
Net *net = net_iter->next();
|
|
if (include_pwr_gnd_
|
|
|| !(network_->isPower(net) || network_->isGround(net))) {
|
|
const char *net_name = network_->name(net);
|
|
if (network_->findPort(cell, net_name) == nullptr) {
|
|
if (isBusName(net_name, '[', ']', escape)) {
|
|
bool is_bus;
|
|
string bus_name;
|
|
int index;
|
|
parseBusName(net_name, '[', ']', escape, is_bus, bus_name, index);
|
|
BusIndexRange &range = bus_ranges[bus_name];
|
|
range.first = max(range.first, index);
|
|
range.second = min(range.second, index);
|
|
}
|
|
else {
|
|
string net_vname = netVerilogName(net_name, network_->pathEscape());
|
|
fprintf(stream_, " wire %s;\n", net_vname.c_str());;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete net_iter;
|
|
|
|
for (const auto& [bus_name1, range] : bus_ranges) {
|
|
const char *bus_name = bus_name1.c_str();
|
|
string net_vname = netVerilogName(bus_name, network_->pathEscape());
|
|
fprintf(stream_, " wire [%d:%d] %s;\n",
|
|
range.first,
|
|
range.second,
|
|
net_vname.c_str());;
|
|
}
|
|
|
|
// Wire net dcls for writeInstBusPinBit.
|
|
int nc_count = findUnconnectedNetCount();
|
|
for (int i = 1; i < nc_count + 1; i++)
|
|
fprintf(stream_, " wire _NC%d;\n", i);
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writeChildren(Instance *inst)
|
|
{
|
|
Vector<Instance*> children;
|
|
InstanceChildIterator *child_iter = network_->childIterator(inst);
|
|
while (child_iter->hasNext()) {
|
|
Instance *child = child_iter->next();
|
|
children.push_back(child);
|
|
if (network_->isHierarchical(child)) {
|
|
pending_children_.push_back(child);
|
|
}
|
|
}
|
|
delete child_iter;
|
|
|
|
if (sort_)
|
|
sort(children, [this](const Instance *inst1,
|
|
const Instance *inst2) {
|
|
return stringLess(network_->name(inst1), network_->name(inst2));
|
|
});
|
|
|
|
for (auto child : children)
|
|
writeChild(child);
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writeChild(Instance *child)
|
|
{
|
|
Cell *child_cell = network_->cell(child);
|
|
if (!remove_cells_.hasKey(child_cell)) {
|
|
const char *child_name = network_->name(child);
|
|
string child_vname = instanceVerilogName(child_name, network_->pathEscape());
|
|
fprintf(stream_, " %s %s (",
|
|
network_->name(child_cell),
|
|
child_vname.c_str());
|
|
bool first_port = true;
|
|
CellPortIterator *port_iter = network_->portIterator(child_cell);
|
|
while (port_iter->hasNext()) {
|
|
Port *port = port_iter->next();
|
|
if (include_pwr_gnd_
|
|
|| !network_->direction(port)->isPowerGround()) {
|
|
if (network_->hasMembers(port))
|
|
writeInstBusPin(child, port, first_port);
|
|
else
|
|
writeInstPin(child, port, first_port);
|
|
}
|
|
}
|
|
delete port_iter;
|
|
fprintf(stream_, ");\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writeInstPin(Instance *inst,
|
|
Port *port,
|
|
bool &first_port)
|
|
{
|
|
Pin *pin = network_->findPin(inst, port);
|
|
if (pin) {
|
|
Net *net = network_->net(pin);
|
|
if (net) {
|
|
const char *net_name = network_->name(net);
|
|
string net_vname = netVerilogName(net_name, network_->pathEscape());
|
|
if (!first_port)
|
|
fprintf(stream_, ",\n ");
|
|
string port_vname = portVerilogName(network_->name(port),
|
|
network_->pathEscape());
|
|
fprintf(stream_, ".%s(%s)",
|
|
port_vname.c_str(),
|
|
net_vname.c_str());
|
|
first_port = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writeInstBusPin(Instance *inst,
|
|
Port *port,
|
|
bool &first_port)
|
|
{
|
|
if (!first_port)
|
|
fprintf(stream_, ",\n ");
|
|
|
|
fprintf(stream_, ".%s({", network_->name(port));
|
|
first_port = false;
|
|
bool first_member = true;
|
|
|
|
// Match the member order of the liberty cell if it exists.
|
|
LibertyPort *lib_port = network_->libertyPort(port);
|
|
if (lib_port) {
|
|
Cell *cell = network_->cell(inst);
|
|
LibertyPortMemberIterator member_iter(lib_port);
|
|
while (member_iter.hasNext()) {
|
|
LibertyPort *lib_member = member_iter.next();
|
|
Port *member = network_->findPort(cell, lib_member->name());
|
|
writeInstBusPinBit(inst, member, first_member);
|
|
}
|
|
}
|
|
else {
|
|
PortMemberIterator *member_iter = network_->memberIterator(port);
|
|
while (member_iter->hasNext()) {
|
|
Port *member = member_iter->next();
|
|
writeInstBusPinBit(inst, member, first_member);
|
|
}
|
|
delete member_iter;
|
|
}
|
|
fprintf(stream_, "})");
|
|
}
|
|
|
|
void
|
|
VerilogWriter::writeInstBusPinBit(Instance *inst,
|
|
Port *port,
|
|
bool &first_member)
|
|
{
|
|
Pin *pin = network_->findPin(inst, port);
|
|
Net *net = pin ? network_->net(pin) : nullptr;
|
|
string net_name;
|
|
if (net)
|
|
net_name = network_->name(net);
|
|
else
|
|
// There is no verilog syntax to "skip" a bit in the concatentation.
|
|
stringPrint(net_name, "_NC%d", unconnected_net_index_++);
|
|
string net_vname = netVerilogName(net_name.c_str(), network_->pathEscape());
|
|
if (!first_member)
|
|
fprintf(stream_, ",\n ");
|
|
fprintf(stream_, "%s", net_vname.c_str());
|
|
first_member = false;
|
|
}
|
|
|
|
// Verilog "ports" are not distinct from nets.
|
|
// Use an assign statement to alias the net when it is connected to
|
|
// multiple output ports.
|
|
void
|
|
VerilogWriter::writeAssigns(Instance *inst)
|
|
{
|
|
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
|
while (pin_iter->hasNext()) {
|
|
Pin *pin = pin_iter->next();
|
|
Term *term = network_->term(pin);
|
|
Net *net = network_->net(term);
|
|
Port *port = network_->port(pin);
|
|
if (port
|
|
&& (include_pwr_gnd_
|
|
|| !(network_->isPower(net) || network_->isGround(net)))
|
|
&& (network_->direction(port)->isAnyOutput()
|
|
|| (include_pwr_gnd_ && network_->direction(port)->isPowerGround()))
|
|
&& !stringEqual(network_->name(port), network_->name(net))) {
|
|
// Port name is different from net name.
|
|
string port_vname = netVerilogName(network_->name(port),
|
|
network_->pathEscape());
|
|
string net_vname = netVerilogName(network_->name(net),
|
|
network_->pathEscape());
|
|
fprintf(stream_, " assign %s = %s;\n",
|
|
port_vname.c_str(),
|
|
net_vname.c_str());
|
|
}
|
|
}
|
|
delete pin_iter;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Walk the hierarch counting unconnected nets used to connect to
|
|
// bus ports with concatenation.
|
|
int
|
|
VerilogWriter::findUnconnectedNetCount()
|
|
{
|
|
return findNCcount(network_->topInstance());
|
|
}
|
|
|
|
int
|
|
VerilogWriter::findNCcount(Instance *inst)
|
|
{
|
|
int nc_count = 0;
|
|
InstanceChildIterator *child_iter = network_->childIterator(inst);
|
|
while (child_iter->hasNext()) {
|
|
Instance *child = child_iter->next();
|
|
nc_count += findChildNCcount(child);
|
|
}
|
|
delete child_iter;
|
|
return nc_count;
|
|
}
|
|
|
|
int
|
|
VerilogWriter::findChildNCcount(Instance *child)
|
|
{
|
|
int nc_count = 0;
|
|
Cell *child_cell = network_->cell(child);
|
|
if (!remove_cells_.hasKey(child_cell)) {
|
|
CellPortIterator *port_iter = network_->portIterator(child_cell);
|
|
while (port_iter->hasNext()) {
|
|
Port *port = port_iter->next();
|
|
if (network_->hasMembers(port))
|
|
nc_count += findPortNCcount(child, port);
|
|
}
|
|
delete port_iter;
|
|
}
|
|
return nc_count;
|
|
}
|
|
|
|
int
|
|
VerilogWriter::findPortNCcount(Instance *inst,
|
|
Port *port)
|
|
{
|
|
int nc_count = 0;
|
|
LibertyPort *lib_port = network_->libertyPort(port);
|
|
if (lib_port) {
|
|
Cell *cell = network_->cell(inst);
|
|
LibertyPortMemberIterator member_iter(lib_port);
|
|
while (member_iter.hasNext()) {
|
|
LibertyPort *lib_member = member_iter.next();
|
|
Port *member = network_->findPort(cell, lib_member->name());
|
|
Pin *pin = network_->findPin(inst, member);
|
|
if (pin == nullptr
|
|
|| network_->net(pin) == nullptr)
|
|
nc_count++;
|
|
}
|
|
}
|
|
return nc_count;
|
|
}
|
|
|
|
} // namespace
|