2013-01-05 04:13:26 -06:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
2015-07-02 04:14:30 -05:00
|
|
|
*
|
2013-01-05 04:13:26 -06:00
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
2015-07-02 04:14:30 -05:00
|
|
|
*
|
2013-01-05 04:13:26 -06:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
* ---
|
|
|
|
*
|
2015-08-14 15:23:01 -05:00
|
|
|
* A simple and straightforward Verilog backend.
|
2013-01-05 04:13:26 -06:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kernel/register.h"
|
|
|
|
#include "kernel/celltypes.h"
|
|
|
|
#include "kernel/log.h"
|
2015-05-08 14:29:51 -05:00
|
|
|
#include "kernel/sigtools.h"
|
2013-01-05 04:13:26 -06:00
|
|
|
#include <string>
|
|
|
|
#include <sstream>
|
|
|
|
#include <set>
|
|
|
|
#include <map>
|
|
|
|
|
2014-09-27 09:17:53 -05:00
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
PRIVATE_NAMESPACE_BEGIN
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2015-06-08 16:35:40 -05:00
|
|
|
bool norename, noattr, attr2comment, noexpr;
|
2013-01-05 04:13:26 -06:00
|
|
|
int auto_name_counter, auto_name_offset, auto_name_digits;
|
2014-08-02 06:11:01 -05:00
|
|
|
std::map<RTLIL::IdString, int> auto_name_map;
|
2014-08-14 08:46:51 -05:00
|
|
|
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
RTLIL::Module *active_module;
|
|
|
|
|
2014-08-02 06:11:01 -05:00
|
|
|
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
const char *str = id.c_str();
|
|
|
|
|
|
|
|
if (*str == '$' && may_rename && !norename)
|
|
|
|
auto_name_map[id] = auto_name_counter++;
|
|
|
|
|
2014-11-07 07:40:06 -06:00
|
|
|
if (str[0] != '\\' || str[1] != '_' || str[2] == 0)
|
2013-01-05 04:13:26 -06:00
|
|
|
return;
|
2014-11-07 07:40:06 -06:00
|
|
|
|
|
|
|
for (int i = 2; str[i] != 0; i++) {
|
|
|
|
if (str[i] == '_' && str[i+1] == 0)
|
2013-01-05 04:13:26 -06:00
|
|
|
continue;
|
|
|
|
if (str[i] < '0' || str[i] > '9')
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-07 07:40:06 -06:00
|
|
|
int num = atoi(str+2);
|
2013-01-05 04:13:26 -06:00
|
|
|
if (num >= auto_name_offset)
|
|
|
|
auto_name_offset = num + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset_auto_counter(RTLIL::Module *module)
|
|
|
|
{
|
|
|
|
auto_name_map.clear();
|
|
|
|
auto_name_counter = 0;
|
|
|
|
auto_name_offset = 0;
|
|
|
|
|
|
|
|
reset_auto_counter_id(module->name, false);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
reset_auto_counter_id(it->second->name, true);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) {
|
2013-01-05 04:13:26 -06:00
|
|
|
reset_auto_counter_id(it->second->name, true);
|
|
|
|
reset_auto_counter_id(it->second->type, false);
|
|
|
|
}
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
reset_auto_counter_id(it->second->name, false);
|
|
|
|
|
|
|
|
auto_name_digits = 1;
|
|
|
|
for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10)
|
|
|
|
auto_name_digits++;
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
log(" renaming `%s' to `_%0*d_'.\n", it->first.c_str(), auto_name_digits, auto_name_offset + it->second);
|
|
|
|
}
|
|
|
|
|
2015-05-20 06:55:50 -05:00
|
|
|
std::string next_auto_id()
|
|
|
|
{
|
|
|
|
return stringf("_%0*d_", auto_name_digits, auto_name_offset + auto_name_counter++);
|
|
|
|
}
|
|
|
|
|
2014-08-02 06:11:01 -05:00
|
|
|
std::string id(RTLIL::IdString internal_id, bool may_rename = true)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
const char *str = internal_id.c_str();
|
|
|
|
bool do_escape = false;
|
|
|
|
|
2015-05-20 06:55:50 -05:00
|
|
|
if (may_rename && auto_name_map.count(internal_id) != 0)
|
|
|
|
return stringf("_%0*d_", auto_name_digits, auto_name_offset + auto_name_map[internal_id]);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (*str == '\\')
|
|
|
|
str++;
|
|
|
|
|
|
|
|
if ('0' <= *str && *str <= '9')
|
|
|
|
do_escape = true;
|
|
|
|
|
|
|
|
for (int i = 0; str[i]; i++)
|
|
|
|
{
|
|
|
|
if ('0' <= str[i] && str[i] <= '9')
|
|
|
|
continue;
|
|
|
|
if ('a' <= str[i] && str[i] <= 'z')
|
|
|
|
continue;
|
|
|
|
if ('A' <= str[i] && str[i] <= 'Z')
|
|
|
|
continue;
|
|
|
|
if (str[i] == '_')
|
|
|
|
continue;
|
|
|
|
do_escape = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_escape)
|
|
|
|
return "\\" + std::string(str) + " ";
|
|
|
|
return std::string(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name)
|
|
|
|
{
|
2014-07-25 07:23:31 -05:00
|
|
|
if (!sig.is_chunk() || sig.as_chunk().wire == NULL)
|
2013-01-05 04:13:26 -06:00
|
|
|
return false;
|
2014-07-25 07:23:31 -05:00
|
|
|
|
|
|
|
RTLIL::SigChunk chunk = sig.as_chunk();
|
|
|
|
|
|
|
|
if (reg_wires.count(chunk.wire->name) == 0)
|
2013-01-05 04:13:26 -06:00
|
|
|
return false;
|
2014-07-25 07:23:31 -05:00
|
|
|
|
|
|
|
reg_name = id(chunk.wire->name);
|
|
|
|
if (sig.size() != chunk.wire->width) {
|
2014-07-22 13:15:14 -05:00
|
|
|
if (sig.size() == 1)
|
2014-07-25 07:23:31 -05:00
|
|
|
reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset);
|
2013-01-05 04:13:26 -06:00
|
|
|
else
|
2014-07-25 07:23:31 -05:00
|
|
|
reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1,
|
|
|
|
chunk.wire->start_offset + chunk.offset);
|
2013-03-03 15:45:06 -06:00
|
|
|
}
|
2014-07-25 07:23:31 -05:00
|
|
|
|
2013-01-05 04:13:26 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-13 15:48:10 -06:00
|
|
|
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
if (width < 0)
|
|
|
|
width = data.bits.size() - offset;
|
2013-12-04 07:14:05 -06:00
|
|
|
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
2013-01-05 04:13:26 -06:00
|
|
|
if (width == 32 && !no_decimal) {
|
2013-08-22 13:22:19 -05:00
|
|
|
int32_t val = 0;
|
2013-01-05 04:13:26 -06:00
|
|
|
for (int i = offset+width-1; i >= offset; i--) {
|
2014-07-28 04:08:55 -05:00
|
|
|
log_assert(i < (int)data.bits.size());
|
2013-01-05 04:13:26 -06:00
|
|
|
if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
|
|
|
|
goto dump_bits;
|
|
|
|
if (data.bits[i] == RTLIL::S1)
|
|
|
|
val |= 1 << (i - offset);
|
|
|
|
}
|
2015-09-25 06:49:26 -05:00
|
|
|
if (set_signed && val < 0)
|
|
|
|
f << stringf("-32'sd %u", -val);
|
|
|
|
else
|
|
|
|
f << stringf("32'%sd %u", set_signed ? "s" : "", val);
|
2013-01-05 04:13:26 -06:00
|
|
|
} else {
|
|
|
|
dump_bits:
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%d'%sb", width, set_signed ? "s" : "");
|
2013-10-24 03:59:27 -05:00
|
|
|
if (width == 0)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("0");
|
2013-01-05 04:13:26 -06:00
|
|
|
for (int i = offset+width-1; i >= offset; i--) {
|
2014-07-28 04:08:55 -05:00
|
|
|
log_assert(i < (int)data.bits.size());
|
2013-01-05 04:13:26 -06:00
|
|
|
switch (data.bits[i]) {
|
2014-08-23 06:54:21 -05:00
|
|
|
case RTLIL::S0: f << stringf("0"); break;
|
|
|
|
case RTLIL::S1: f << stringf("1"); break;
|
|
|
|
case RTLIL::Sx: f << stringf("x"); break;
|
|
|
|
case RTLIL::Sz: f << stringf("z"); break;
|
|
|
|
case RTLIL::Sa: f << stringf("z"); break;
|
2013-01-05 04:13:26 -06:00
|
|
|
case RTLIL::Sm: log_error("Found marker state in final netlist.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\"");
|
2013-12-04 07:14:05 -06:00
|
|
|
std::string str = data.decode_string();
|
|
|
|
for (size_t i = 0; i < str.size(); i++) {
|
|
|
|
if (str[i] == '\n')
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\\n");
|
2013-12-04 07:14:05 -06:00
|
|
|
else if (str[i] == '\t')
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\\t");
|
2013-12-04 07:14:05 -06:00
|
|
|
else if (str[i] < 32)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\\%03o", str[i]);
|
2013-12-04 07:14:05 -06:00
|
|
|
else if (str[i] == '"')
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\\\"");
|
2013-12-04 07:14:05 -06:00
|
|
|
else if (str[i] == '\\')
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\\\\");
|
2015-02-13 15:48:10 -06:00
|
|
|
else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*')
|
|
|
|
f << stringf("\\/");
|
2013-01-05 04:13:26 -06:00
|
|
|
else
|
2014-08-23 06:54:21 -05:00
|
|
|
f << str[i];
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\"");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
if (chunk.wire == NULL) {
|
|
|
|
dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
|
|
|
|
} else {
|
2014-07-28 07:25:03 -05:00
|
|
|
if (chunk.width == chunk.wire->width && chunk.offset == 0) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s", id(chunk.wire->name).c_str());
|
2014-07-28 07:25:03 -05:00
|
|
|
} else if (chunk.width == 1) {
|
|
|
|
if (chunk.wire->upto)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
|
2014-07-28 07:25:03 -05:00
|
|
|
else
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset);
|
2014-07-28 07:25:03 -05:00
|
|
|
} else {
|
|
|
|
if (chunk.wire->upto)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
|
2014-07-28 07:25:03 -05:00
|
|
|
(chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
|
|
|
|
(chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
|
|
|
|
else
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
|
2014-07-28 07:25:03 -05:00
|
|
|
(chunk.offset + chunk.width - 1) + chunk.wire->start_offset,
|
|
|
|
chunk.offset + chunk.wire->start_offset);
|
|
|
|
}
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-07-25 07:23:31 -05:00
|
|
|
if (sig.is_chunk()) {
|
|
|
|
dump_sigchunk(f, sig.as_chunk());
|
2013-01-05 04:13:26 -06:00
|
|
|
} else {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("{ ");
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
|
2014-07-22 13:15:14 -05:00
|
|
|
if (it != sig.chunks().rbegin())
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(", ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigchunk(f, *it, true);
|
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" }");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-02 15:46:20 -06:00
|
|
|
void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
if (noattr)
|
|
|
|
return;
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
|
|
|
|
f << stringf(" = ");
|
2015-01-02 15:46:20 -06:00
|
|
|
if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
|
|
|
|
f << stringf(" 0 ");
|
|
|
|
else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
|
|
|
|
f << stringf(" 1 ");
|
|
|
|
else
|
2015-02-13 15:48:10 -06:00
|
|
|
dump_const(f, it->second, -1, 0, false, false, attr2comment);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" %s%c", attr2comment ? "*/" : "*)", term);
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
dump_attributes(f, indent, wire->attributes);
|
2013-03-21 03:51:25 -05:00
|
|
|
#if 0
|
2013-01-05 04:13:26 -06:00
|
|
|
if (wire->port_input && !wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
2013-01-05 04:13:26 -06:00
|
|
|
else if (!wire->port_input && wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
2013-01-05 04:13:26 -06:00
|
|
|
else if (wire->port_input && wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
2013-01-05 04:13:26 -06:00
|
|
|
else
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire");
|
2013-01-05 04:13:26 -06:00
|
|
|
if (wire->width != 1)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset);
|
|
|
|
f << stringf("%s;\n", id(wire->name).c_str());
|
2013-03-21 03:51:25 -05:00
|
|
|
#else
|
2015-08-14 03:56:05 -05:00
|
|
|
// do not use Verilog-2k "output reg" syntax in Verilog export
|
2013-03-21 03:51:25 -05:00
|
|
|
std::string range = "";
|
2014-07-28 07:25:03 -05:00
|
|
|
if (wire->width != 1) {
|
|
|
|
if (wire->upto)
|
|
|
|
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);
|
|
|
|
}
|
2013-03-21 03:51:25 -05:00
|
|
|
if (wire->port_input && !wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
2013-03-21 03:51:25 -05:00
|
|
|
if (!wire->port_input && wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
2013-03-21 03:51:25 -05:00
|
|
|
if (wire->port_input && wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
2015-04-09 08:12:26 -05:00
|
|
|
if (reg_wires.count(wire->name)) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
2015-04-09 08:12:26 -05:00
|
|
|
if (wire->attributes.count("\\init")) {
|
|
|
|
f << stringf("%s" "initial %s = ", indent.c_str(), id(wire->name).c_str());
|
|
|
|
dump_const(f, wire->attributes.at("\\init"));
|
|
|
|
f << stringf(";\n");
|
|
|
|
}
|
|
|
|
} else if (!wire->port_input && !wire->port_output)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
2013-03-21 03:51:25 -05:00
|
|
|
#endif
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
dump_attributes(f, indent, memory->attributes);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1);
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("$signed(");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\" + port));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")");
|
2013-01-05 04:13:26 -06:00
|
|
|
} else
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\" + port));
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string cellname(RTLIL::Cell *cell)
|
|
|
|
{
|
2014-08-14 08:46:51 -05:00
|
|
|
if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort("\\Q"))
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-07-31 09:38:54 -05:00
|
|
|
RTLIL::SigSpec sig = cell->getPort("\\Q");
|
2014-10-10 09:59:44 -05:00
|
|
|
if (GetSize(sig) != 1 || sig.is_fully_const())
|
2013-01-05 04:13:26 -06:00
|
|
|
goto no_special_reg_name;
|
|
|
|
|
2014-07-25 07:23:31 -05:00
|
|
|
RTLIL::Wire *wire = sig[0].wire;
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (wire->name[0] != '\\')
|
|
|
|
goto no_special_reg_name;
|
|
|
|
|
2014-08-02 06:11:01 -05:00
|
|
|
std::string cell_name = wire->name.str();
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
size_t pos = cell_name.find('[');
|
|
|
|
if (pos != std::string::npos)
|
|
|
|
cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos);
|
|
|
|
else
|
|
|
|
cell_name = cell_name + "_reg";
|
|
|
|
|
|
|
|
if (wire->width != 1)
|
2014-07-25 07:23:31 -05:00
|
|
|
cell_name += stringf("[%d]", wire->start_offset + sig[0].offset);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (active_module && active_module->count_id(cell_name) > 0)
|
|
|
|
goto no_special_reg_name;
|
|
|
|
|
|
|
|
return id(cell_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
no_special_reg_name:
|
|
|
|
return id(cell->name).c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = %s ", op.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
|
|
|
dump_cell_expr_port(f, cell, "A", true);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "A", true);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" %s ", op.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
|
|
|
dump_cell_expr_port(f, cell, "B", true);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-08-15 07:11:40 -05:00
|
|
|
if (cell->type == "$_NOT_") {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
|
|
|
f << stringf("~");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
|
|
|
dump_cell_expr_port(f, cell, "A", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("~(");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "A", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" ");
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_AND_", "$_NAND_"))
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("&");
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_OR_", "$_NOR_"))
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("|");
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_XOR_", "$_XNOR_"))
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("^");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "B", false);
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")");
|
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cell->type == "$_MUX_") {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "S", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" ? ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
|
|
|
dump_cell_expr_port(f, cell, "B", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" : ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "A", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-16 11:18:30 -05:00
|
|
|
if (cell->type.in("$_AOI3_", "$_OAI3_")) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ~((");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "A", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(cell->type == "$_AOI3_" ? " & " : " | ");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "B", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(cell->type == "$_AOI3_" ? ") |" : ") &");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" ");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "C", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(");\n");
|
2014-08-16 11:18:30 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cell->type.in("$_AOI4_", "$_OAI4_")) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ~((");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "A", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "B", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(cell->type == "$_AOI4_" ? ") |" : ") &");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" (");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "C", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
|
2014-08-16 11:18:30 -05:00
|
|
|
dump_cell_expr_port(f, cell, "D", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("));\n");
|
2014-08-16 11:18:30 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-01-05 04:13:26 -06:00
|
|
|
if (cell->type.substr(0, 6) == "$_DFF_")
|
|
|
|
{
|
|
|
|
std::string reg_name = cellname(cell);
|
2014-07-31 09:38:54 -05:00
|
|
|
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (!out_is_reg_wire)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
dump_attributes(f, indent, cell->attributes);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\C"));
|
2013-01-05 04:13:26 -06:00
|
|
|
if (cell->type[7] != '_') {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\R"));
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (cell->type[7] != '_') {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\R"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
|
|
|
f << stringf("%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
|
|
|
|
f << stringf("%s" " else\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "D", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (!out_is_reg_wire) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Q"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = %s;\n", reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-11-21 14:52:30 -06:00
|
|
|
if (cell->type.substr(0, 8) == "$_DFFSR_")
|
|
|
|
{
|
|
|
|
char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
|
|
|
|
|
|
|
|
std::string reg_name = cellname(cell);
|
2014-07-31 09:38:54 -05:00
|
|
|
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
2013-11-21 14:52:30 -06:00
|
|
|
|
|
|
|
if (!out_is_reg_wire)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
2013-11-21 14:52:30 -06:00
|
|
|
|
|
|
|
dump_attributes(f, indent, cell->attributes);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\C"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\S"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\R"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
2013-11-21 14:52:30 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\R"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
|
|
|
f << stringf("%s" " %s <= 0;\n", indent.c_str(), reg_name.c_str());
|
2013-11-21 14:52:30 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " else if (%s", indent.c_str(), pol_s == 'P' ? "" : "!");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\S"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
|
|
|
f << stringf("%s" " %s <= 1;\n", indent.c_str(), reg_name.c_str());
|
2013-11-21 14:52:30 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " else\n", indent.c_str());
|
|
|
|
f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
2013-11-21 14:52:30 -06:00
|
|
|
dump_cell_expr_port(f, cell, "D", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-11-21 14:52:30 -06:00
|
|
|
|
|
|
|
if (!out_is_reg_wire) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Q"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = %s;\n", reg_name.c_str());
|
2013-11-21 14:52:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-01-05 04:13:26 -06:00
|
|
|
#define HANDLE_UNIOP(_type, _operator) \
|
|
|
|
if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
|
|
|
|
#define HANDLE_BINOP(_type, _operator) \
|
|
|
|
if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
|
|
|
|
|
|
|
|
HANDLE_UNIOP("$not", "~")
|
|
|
|
HANDLE_UNIOP("$pos", "+")
|
|
|
|
HANDLE_UNIOP("$neg", "-")
|
|
|
|
|
|
|
|
HANDLE_BINOP("$and", "&")
|
|
|
|
HANDLE_BINOP("$or", "|")
|
|
|
|
HANDLE_BINOP("$xor", "^")
|
|
|
|
HANDLE_BINOP("$xnor", "~^")
|
|
|
|
|
|
|
|
HANDLE_UNIOP("$reduce_and", "&")
|
|
|
|
HANDLE_UNIOP("$reduce_or", "|")
|
|
|
|
HANDLE_UNIOP("$reduce_xor", "^")
|
|
|
|
HANDLE_UNIOP("$reduce_xnor", "~^")
|
|
|
|
HANDLE_UNIOP("$reduce_bool", "|")
|
|
|
|
|
|
|
|
HANDLE_BINOP("$shl", "<<")
|
|
|
|
HANDLE_BINOP("$shr", ">>")
|
|
|
|
HANDLE_BINOP("$sshl", "<<<")
|
|
|
|
HANDLE_BINOP("$sshr", ">>>")
|
|
|
|
|
2013-12-27 07:20:15 -06:00
|
|
|
HANDLE_BINOP("$lt", "<")
|
|
|
|
HANDLE_BINOP("$le", "<=")
|
|
|
|
HANDLE_BINOP("$eq", "==")
|
|
|
|
HANDLE_BINOP("$ne", "!=")
|
|
|
|
HANDLE_BINOP("$eqx", "===")
|
|
|
|
HANDLE_BINOP("$nex", "!==")
|
|
|
|
HANDLE_BINOP("$ge", ">=")
|
|
|
|
HANDLE_BINOP("$gt", ">")
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
HANDLE_BINOP("$add", "+")
|
|
|
|
HANDLE_BINOP("$sub", "-")
|
|
|
|
HANDLE_BINOP("$mul", "*")
|
|
|
|
HANDLE_BINOP("$div", "/")
|
|
|
|
HANDLE_BINOP("$mod", "%")
|
|
|
|
HANDLE_BINOP("$pow", "**")
|
|
|
|
|
|
|
|
HANDLE_UNIOP("$logic_not", "!")
|
|
|
|
HANDLE_BINOP("$logic_and", "&&")
|
|
|
|
HANDLE_BINOP("$logic_or", "||")
|
|
|
|
|
|
|
|
#undef HANDLE_UNIOP
|
|
|
|
#undef HANDLE_BINOP
|
|
|
|
|
2014-08-02 14:10:08 -05:00
|
|
|
if (cell->type == "$mux")
|
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-08-02 14:10:08 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2014-08-02 14:10:08 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\S"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" ? ");
|
2014-08-02 14:10:08 -05:00
|
|
|
dump_attributes(f, "", cell->attributes, ' ');
|
|
|
|
dump_sigspec(f, cell->getPort("\\B"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" : ");
|
2014-08-02 14:10:08 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\A"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2014-08-02 14:10:08 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cell->type == "$pmux" || cell->type == "$pmux_safe")
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
int width = cell->parameters["\\WIDTH"].as_int();
|
2014-07-31 09:38:54 -05:00
|
|
|
int s_width = cell->getPort("\\S").size();
|
2014-07-19 19:16:30 -05:00
|
|
|
std::string func_name = cellname(cell);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str());
|
|
|
|
f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1);
|
|
|
|
f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1);
|
|
|
|
f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1);
|
2014-07-19 19:16:30 -05:00
|
|
|
|
|
|
|
dump_attributes(f, indent + " ", cell->attributes);
|
2014-08-02 14:10:08 -05:00
|
|
|
if (cell->type != "$pmux_safe" && !noattr)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " (* parallel_case *)\n", indent.c_str());
|
|
|
|
f << stringf("%s" " casez (s)", indent.c_str());
|
2014-08-02 14:10:08 -05:00
|
|
|
if (cell->type != "$pmux_safe")
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
for (int i = 0; i < s_width; i++)
|
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " %d'b", indent.c_str(), s_width);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
for (int j = s_width-1; j >= 0; j--)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?');
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(":\n");
|
|
|
|
f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width);
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " default:\n", indent.c_str());
|
|
|
|
f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " endcase\n", indent.c_str());
|
|
|
|
f << stringf("%s" "endfunction\n", indent.c_str());
|
2014-07-19 19:16:30 -05:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = %s(", func_name.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\A"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(", ");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\B"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(", ");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\S"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(");\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-02-07 10:44:57 -06:00
|
|
|
if (cell->type == "$slice")
|
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\A"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" >> %d;\n", cell->parameters.at("\\OFFSET").as_int());
|
2014-02-07 10:44:57 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cell->type == "$concat")
|
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Y"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = { ");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\B"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" , ");
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\A"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" };\n");
|
2014-02-07 10:44:57 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-18 02:01:37 -05:00
|
|
|
if (cell->type == "$dffsr")
|
|
|
|
{
|
|
|
|
SigSpec sig_clk = cell->getPort("\\CLK");
|
|
|
|
SigSpec sig_set = cell->getPort("\\SET");
|
|
|
|
SigSpec sig_clr = cell->getPort("\\CLR");
|
|
|
|
SigSpec sig_d = cell->getPort("\\D");
|
|
|
|
SigSpec sig_q = cell->getPort("\\Q");
|
|
|
|
|
|
|
|
int width = cell->parameters["\\WIDTH"].as_int();
|
|
|
|
bool pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
|
|
|
|
bool pol_set = cell->parameters["\\SET_POLARITY"].as_bool();
|
|
|
|
bool pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool();
|
|
|
|
|
|
|
|
std::string reg_name = cellname(cell);
|
|
|
|
bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
|
|
|
|
|
|
|
|
if (!out_is_reg_wire)
|
|
|
|
f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str());
|
|
|
|
|
|
|
|
for (int i = 0; i < width; i++) {
|
|
|
|
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
|
|
|
dump_sigspec(f, sig_clk);
|
|
|
|
f << stringf(", %sedge ", pol_set ? "pos" : "neg");
|
|
|
|
dump_sigspec(f, sig_set);
|
|
|
|
f << stringf(", %sedge ", pol_clr ? "pos" : "neg");
|
|
|
|
dump_sigspec(f, sig_clr);
|
|
|
|
f << stringf(")\n");
|
|
|
|
|
|
|
|
f << stringf("%s" " if (%s", indent.c_str(), pol_clr ? "" : "!");
|
|
|
|
dump_sigspec(f, sig_clr);
|
|
|
|
f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i);
|
|
|
|
|
|
|
|
f << stringf("%s" " else if (%s", indent.c_str(), pol_set ? "" : "!");
|
|
|
|
dump_sigspec(f, sig_set);
|
|
|
|
f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i);
|
|
|
|
|
|
|
|
f << stringf("%s" " else %s[%d] <= ", indent.c_str(), reg_name.c_str(), i);
|
|
|
|
dump_sigspec(f, sig_d[i]);
|
|
|
|
f << stringf(";\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!out_is_reg_wire) {
|
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
|
|
|
dump_sigspec(f, sig_q);
|
|
|
|
f << stringf(" = %s;\n", reg_name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-19 17:03:20 -06:00
|
|
|
if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe")
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-12-19 17:03:20 -06:00
|
|
|
RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
|
|
|
|
bool pol_clk, pol_arst = false, pol_en = false;
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-07-31 09:38:54 -05:00
|
|
|
sig_clk = cell->getPort("\\CLK");
|
2013-01-05 04:13:26 -06:00
|
|
|
pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
|
|
|
|
|
|
|
|
if (cell->type == "$adff") {
|
2014-07-31 09:38:54 -05:00
|
|
|
sig_arst = cell->getPort("\\ARST");
|
2013-01-05 04:13:26 -06:00
|
|
|
pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool();
|
|
|
|
val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]);
|
|
|
|
}
|
|
|
|
|
2014-12-19 17:03:20 -06:00
|
|
|
if (cell->type == "$dffe") {
|
|
|
|
sig_en = cell->getPort("\\EN");
|
|
|
|
pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
|
|
|
|
}
|
|
|
|
|
2013-01-05 04:13:26 -06:00
|
|
|
std::string reg_name = cellname(cell);
|
2014-07-31 09:38:54 -05:00
|
|
|
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (!out_is_reg_wire)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sig_clk);
|
|
|
|
if (cell->type == "$adff") {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" or %sedge ", pol_arst ? "pos" : "neg");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sig_arst);
|
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (cell->type == "$adff") {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sig_arst);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
|
|
|
f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, val_arst);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
|
|
|
f << stringf("%s" " else\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-12-19 17:03:20 -06:00
|
|
|
if (cell->type == "$dffe") {
|
|
|
|
f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
|
|
|
|
dump_sigspec(f, sig_en);
|
|
|
|
f << stringf(")\n");
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell_expr_port(f, cell, "D", false);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (!out_is_reg_wire) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2014-07-31 09:38:54 -05:00
|
|
|
dump_sigspec(f, cell->getPort("\\Q"));
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = %s;\n", reg_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-08 16:35:40 -05:00
|
|
|
if (cell->type == "$mem")
|
2015-05-07 12:03:09 -05:00
|
|
|
{
|
|
|
|
RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
|
2015-05-10 14:38:41 -05:00
|
|
|
std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
|
2015-05-07 12:03:09 -05:00
|
|
|
int abits = cell->parameters["\\ABITS"].as_int();
|
|
|
|
int size = cell->parameters["\\SIZE"].as_int();
|
|
|
|
int width = cell->parameters["\\WIDTH"].as_int();
|
2015-05-10 14:38:41 -05:00
|
|
|
bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
|
2015-05-07 12:03:09 -05:00
|
|
|
|
|
|
|
// for memory block make something like:
|
|
|
|
// reg [7:0] memid [3:0];
|
|
|
|
// initial begin
|
|
|
|
// memid[0] <= ...
|
|
|
|
// end
|
2015-06-04 13:56:13 -05:00
|
|
|
f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0);
|
2015-05-07 12:03:09 -05:00
|
|
|
if (use_init)
|
|
|
|
{
|
2015-05-08 14:29:51 -05:00
|
|
|
f << stringf("%s" "initial begin\n", indent.c_str());
|
2015-05-07 12:03:09 -05:00
|
|
|
for (int i=0; i<size; i++)
|
|
|
|
{
|
2015-09-25 06:49:26 -05:00
|
|
|
f << stringf("%s" " %s[%d] <= ", indent.c_str(), mem_id.c_str(), i);
|
|
|
|
dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
|
|
|
|
f << stringf(";\n");
|
2015-05-07 12:03:09 -05:00
|
|
|
}
|
2015-05-08 14:29:51 -05:00
|
|
|
f << stringf("%s" "end\n", indent.c_str());
|
2015-05-07 12:03:09 -05:00
|
|
|
}
|
|
|
|
|
2015-06-08 16:35:40 -05:00
|
|
|
// create a map : "edge clk" -> expressions within that clock domain
|
|
|
|
dict<std::string, std::vector<std::string>> clk_to_lof_body;
|
|
|
|
clk_to_lof_body[""] = std::vector<std::string>();
|
|
|
|
std::string clk_domain_str;
|
|
|
|
// create a list of reg declarations
|
|
|
|
std::vector<std::string> lof_reg_declarations;
|
|
|
|
|
2015-05-07 12:03:09 -05:00
|
|
|
int nread_ports = cell->parameters["\\RD_PORTS"].as_int();
|
|
|
|
RTLIL::SigSpec sig_rd_clk, sig_rd_data, sig_rd_addr;
|
2015-05-08 14:29:51 -05:00
|
|
|
bool use_rd_clk, rd_clk_posedge, rd_transparent;
|
2015-05-07 12:03:09 -05:00
|
|
|
// read ports
|
|
|
|
for (int i=0; i < nread_ports; i++)
|
|
|
|
{
|
|
|
|
sig_rd_clk = cell->getPort("\\RD_CLK").extract(i);
|
|
|
|
sig_rd_data = cell->getPort("\\RD_DATA").extract(i*width, width);
|
|
|
|
sig_rd_addr = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
|
|
|
|
use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool();
|
|
|
|
rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool();
|
2015-05-08 14:29:51 -05:00
|
|
|
rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool();
|
2015-06-08 16:35:40 -05:00
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
dump_sigspec(os, sig_rd_clk);
|
|
|
|
clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
|
|
|
|
if( clk_to_lof_body.count(clk_domain_str) == 0 )
|
|
|
|
clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
|
|
|
|
}
|
2015-05-08 14:29:51 -05:00
|
|
|
if (use_rd_clk && !rd_transparent)
|
2015-05-07 12:03:09 -05:00
|
|
|
{
|
|
|
|
// for clocked read ports make something like:
|
2015-05-20 06:55:50 -05:00
|
|
|
// reg [..] temp_id;
|
2015-05-07 12:03:09 -05:00
|
|
|
// always @(posedge clk)
|
2015-05-20 06:55:50 -05:00
|
|
|
// temp_id <= array_reg[r_addr];
|
|
|
|
// assign r_data = temp_id;
|
|
|
|
std::string temp_id = next_auto_id();
|
2015-06-08 16:35:40 -05:00
|
|
|
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
dump_sigspec(os, sig_rd_addr);
|
|
|
|
std::string line = stringf("%s <= %s[%s];\n", temp_id.c_str(), mem_id.c_str(), os.str().c_str());
|
|
|
|
clk_to_lof_body[clk_domain_str].push_back(line);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
dump_sigspec(os, sig_rd_data);
|
|
|
|
std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
|
|
|
|
clk_to_lof_body[""].push_back(line);
|
|
|
|
}
|
2015-05-10 14:38:41 -05:00
|
|
|
} else {
|
|
|
|
if (rd_transparent) {
|
2015-05-08 14:29:51 -05:00
|
|
|
// for rd-transparent read-ports make something like:
|
2015-05-20 06:55:50 -05:00
|
|
|
// reg [..] temp_id;
|
2015-05-08 14:29:51 -05:00
|
|
|
// always @(posedge clk)
|
2015-05-20 06:55:50 -05:00
|
|
|
// temp_id <= r_addr;
|
|
|
|
// assign r_data = array_reg[temp_id];
|
|
|
|
std::string temp_id = next_auto_id();
|
2015-06-08 16:35:40 -05:00
|
|
|
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_addr.size() - 1, temp_id.c_str()) );
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
dump_sigspec(os, sig_rd_addr);
|
|
|
|
std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
|
|
|
|
clk_to_lof_body[clk_domain_str].push_back(line);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
dump_sigspec(os, sig_rd_data);
|
|
|
|
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str());
|
2015-09-25 06:49:26 -05:00
|
|
|
clk_to_lof_body[""].push_back(line);
|
2015-06-08 16:35:40 -05:00
|
|
|
}
|
2015-05-10 14:38:41 -05:00
|
|
|
} else {
|
2015-05-08 14:29:51 -05:00
|
|
|
// for non-clocked read-ports make something like:
|
|
|
|
// assign r_data = array_reg[r_addr];
|
2015-06-08 16:35:40 -05:00
|
|
|
std::ostringstream os, os2;
|
|
|
|
dump_sigspec(os, sig_rd_data);
|
|
|
|
dump_sigspec(os2, sig_rd_addr);
|
|
|
|
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
|
|
|
|
clk_to_lof_body[""].push_back(line);
|
2015-05-08 14:29:51 -05:00
|
|
|
}
|
2015-05-07 12:03:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
|
2015-05-10 10:33:24 -05:00
|
|
|
RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en, sig_wr_en_bit;
|
2015-06-04 13:56:13 -05:00
|
|
|
RTLIL::SigBit last_bit;
|
2015-05-10 10:33:24 -05:00
|
|
|
bool wr_clk_posedge;
|
|
|
|
RTLIL::SigSpec lof_wen;
|
|
|
|
dict<RTLIL::SigSpec, int> wen_to_width;
|
2015-05-08 14:29:51 -05:00
|
|
|
SigMap sigmap(active_module);
|
|
|
|
int n, wen_width;
|
2015-05-07 12:03:09 -05:00
|
|
|
// write ports
|
|
|
|
for (int i=0; i < nwrite_ports; i++)
|
|
|
|
{
|
|
|
|
sig_wr_clk = cell->getPort("\\WR_CLK").extract(i);
|
|
|
|
sig_wr_data = cell->getPort("\\WR_DATA").extract(i*width, width);
|
|
|
|
sig_wr_addr = cell->getPort("\\WR_ADDR").extract(i*abits, abits);
|
|
|
|
sig_wr_en = cell->getPort("\\WR_EN").extract(i*width, width);
|
|
|
|
sig_wr_en_bit = sig_wr_en.extract(0);
|
|
|
|
wr_clk_posedge = cell->parameters["\\WR_CLK_POLARITY"].extract(i).as_bool();
|
2015-06-08 16:35:40 -05:00
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
dump_sigspec(os, sig_wr_clk);
|
|
|
|
clk_domain_str = stringf("%sedge %s", wr_clk_posedge ? "pos" : "neg", os.str().c_str());
|
|
|
|
if( clk_to_lof_body.count(clk_domain_str) == 0 )
|
|
|
|
clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
|
|
|
|
}
|
2015-05-08 14:29:51 -05:00
|
|
|
// group the wen bits
|
|
|
|
last_bit = sig_wr_en.extract(0);
|
2015-05-11 13:05:18 -05:00
|
|
|
lof_wen = RTLIL::SigSpec(last_bit);
|
|
|
|
wen_to_width.clear();
|
2015-05-08 14:29:51 -05:00
|
|
|
wen_to_width[last_bit] = 0;
|
2015-06-04 13:56:13 -05:00
|
|
|
for (auto ¤t_bit : sig_wr_en.bits())
|
2015-05-07 12:03:09 -05:00
|
|
|
{
|
2015-05-10 14:38:41 -05:00
|
|
|
if (sigmap(current_bit) == sigmap(last_bit)){
|
2015-05-10 10:33:24 -05:00
|
|
|
wen_to_width[current_bit] += 1;
|
2015-05-10 14:38:41 -05:00
|
|
|
} else {
|
2015-05-10 10:33:24 -05:00
|
|
|
lof_wen.append_bit(current_bit);
|
2015-05-08 14:29:51 -05:00
|
|
|
wen_to_width[current_bit] = 1;
|
2015-05-07 12:03:09 -05:00
|
|
|
}
|
2015-05-08 14:29:51 -05:00
|
|
|
last_bit = current_bit;
|
2015-05-07 12:03:09 -05:00
|
|
|
}
|
2015-05-08 14:29:51 -05:00
|
|
|
// make something like:
|
|
|
|
// always @(posedge clk)
|
2015-06-08 16:35:40 -05:00
|
|
|
// if (wr_en_bit) memid[w_addr][??] <= w_data[??];
|
2015-05-08 14:29:51 -05:00
|
|
|
// ...
|
|
|
|
n = 0;
|
|
|
|
for (auto &wen_bit : lof_wen) {
|
|
|
|
wen_width = wen_to_width[wen_bit];
|
2015-05-10 14:38:41 -05:00
|
|
|
if (!(wen_bit == RTLIL::SigBit(false)))
|
2015-05-08 14:29:51 -05:00
|
|
|
{
|
2015-06-08 16:35:40 -05:00
|
|
|
std::ostringstream os;
|
2015-05-10 14:38:41 -05:00
|
|
|
if (!(wen_bit == RTLIL::SigBit(true)))
|
2015-05-08 14:29:51 -05:00
|
|
|
{
|
2015-06-08 16:35:40 -05:00
|
|
|
os << stringf("if (");
|
|
|
|
dump_sigspec(os, wen_bit);
|
|
|
|
os << stringf(") ");
|
2015-05-08 14:29:51 -05:00
|
|
|
}
|
2015-06-08 16:35:40 -05:00
|
|
|
os << stringf("%s[", mem_id.c_str());
|
|
|
|
dump_sigspec(os, sig_wr_addr);
|
2015-05-08 14:29:51 -05:00
|
|
|
if (wen_width == width)
|
2015-06-08 16:35:40 -05:00
|
|
|
os << stringf("] <= ");
|
2015-05-08 14:29:51 -05:00
|
|
|
else
|
2015-06-08 16:35:40 -05:00
|
|
|
os << stringf("][%d:%d] <= ", n+wen_width-1, n);
|
|
|
|
dump_sigspec(os, sig_wr_data.extract(n, wen_width));
|
|
|
|
os << stringf(";\n");
|
|
|
|
clk_to_lof_body[clk_domain_str].push_back(os.str());
|
2015-05-08 14:29:51 -05:00
|
|
|
}
|
|
|
|
n += wen_width;
|
2015-05-07 12:03:09 -05:00
|
|
|
}
|
|
|
|
}
|
2015-08-14 15:23:01 -05:00
|
|
|
// Output Verilog that looks something like this:
|
2015-07-02 04:14:30 -05:00
|
|
|
// reg [..] _3_;
|
2015-06-08 16:35:40 -05:00
|
|
|
// always @(posedge CLK2) begin
|
|
|
|
// _3_ <= memory[D1ADDR];
|
|
|
|
// if (A1EN)
|
|
|
|
// memory[A1ADDR] <= A1DATA;
|
|
|
|
// if (A2EN)
|
|
|
|
// memory[A2ADDR] <= A2DATA;
|
|
|
|
// ...
|
|
|
|
// end
|
|
|
|
// always @(negedge CLK1) begin
|
|
|
|
// if (C1EN)
|
|
|
|
// memory[C1ADDR] <= C1DATA;
|
|
|
|
// end
|
|
|
|
// ...
|
|
|
|
// assign D1DATA = _3_;
|
|
|
|
// assign D2DATA <= memory[D2ADDR];
|
|
|
|
|
|
|
|
// the reg ... definitions
|
|
|
|
for(auto ® : lof_reg_declarations)
|
|
|
|
{
|
|
|
|
f << stringf("%s" "%s", indent.c_str(), reg.c_str());
|
|
|
|
}
|
|
|
|
// the block of expressions by clock domain
|
|
|
|
for(auto &pair : clk_to_lof_body)
|
|
|
|
{
|
|
|
|
std::string clk_domain = pair.first;
|
|
|
|
std::vector<std::string> lof_lines = pair.second;
|
|
|
|
if( clk_domain != "")
|
|
|
|
{
|
|
|
|
f << stringf("%s" "always @(%s) begin\n", indent.c_str(), clk_domain.c_str());
|
|
|
|
for(auto &line : lof_lines)
|
|
|
|
f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
|
|
|
|
f << stringf("%s" "end\n", indent.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// the non-clocked assignments
|
|
|
|
for(auto &line : lof_lines)
|
|
|
|
f << stringf("%s" "%s", indent.c_str(), line.c_str());
|
|
|
|
}
|
|
|
|
}
|
2015-05-08 14:29:51 -05:00
|
|
|
|
2015-05-07 12:03:09 -05:00
|
|
|
return true;
|
|
|
|
}
|
2015-07-02 04:14:30 -05:00
|
|
|
|
2014-08-14 08:46:51 -05:00
|
|
|
// FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
|
2015-05-07 12:03:09 -05:00
|
|
|
// FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
if (cell->type[0] == '$' && !noexpr) {
|
|
|
|
if (dump_cell_expr(f, indent, cell))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dump_attributes(f, indent, cell->attributes);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (cell->parameters.size() > 0) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" #(");
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
|
2013-01-05 04:13:26 -06:00
|
|
|
if (it != cell->parameters.begin())
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(",");
|
|
|
|
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
|
2013-12-04 07:24:44 -06:00
|
|
|
bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
|
2015-01-02 15:46:20 -06:00
|
|
|
dump_const(f, it->second, -1, 0, false, is_signed);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\n%s" ")", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string cell_name = cellname(cell);
|
|
|
|
if (cell_name != id(cell->name))
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
else
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" %s (", cell_name.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
bool first_arg = true;
|
2014-08-02 06:11:01 -05:00
|
|
|
std::set<RTLIL::IdString> numbered_ports;
|
2013-01-05 04:13:26 -06:00
|
|
|
for (int i = 1; true; i++) {
|
|
|
|
char str[16];
|
|
|
|
snprintf(str, 16, "$%d", i);
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
|
2013-01-05 04:13:26 -06:00
|
|
|
if (it->first != str)
|
|
|
|
continue;
|
|
|
|
if (!first_arg)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(",");
|
2013-01-05 04:13:26 -06:00
|
|
|
first_arg = false;
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\n%s ", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, it->second);
|
|
|
|
numbered_ports.insert(it->first);
|
|
|
|
goto found_numbered_port;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
found_numbered_port:;
|
|
|
|
}
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
|
2013-01-05 04:13:26 -06:00
|
|
|
if (numbered_ports.count(it->first))
|
|
|
|
continue;
|
|
|
|
if (!first_arg)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(",");
|
2013-01-05 04:13:26 -06:00
|
|
|
first_arg = false;
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
|
2014-07-22 13:15:14 -05:00
|
|
|
if (it->second.size() > 0)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, it->second);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\n%s" ");\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "assign ", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, left);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, right);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
int number_of_stmts = cs->switches.size() + cs->actions.size();
|
|
|
|
|
|
|
|
if (!omit_trailing_begin && number_of_stmts >= 2)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "begin\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
|
2014-07-22 13:15:14 -05:00
|
|
|
if (it->first.size() == 0)
|
2013-01-05 04:13:26 -06:00
|
|
|
continue;
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s ", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, it->first);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" = ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, it->second);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_proc_switch(f, indent + " ", *it);
|
|
|
|
|
|
|
|
if (!omit_trailing_begin && number_of_stmts == 0)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s /* empty */;\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
if (omit_trailing_begin || number_of_stmts >= 2)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "end\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2014-07-22 13:15:14 -05:00
|
|
|
if (sw->signal.size() == 0) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "begin\n", indent.c_str());
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
|
2013-01-05 04:13:26 -06:00
|
|
|
if ((*it)->compare.size() == 0)
|
|
|
|
dump_case_body(f, indent + " ", *it);
|
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "end\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "casez (", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sw->signal);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(")\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2016-03-14 07:03:28 -05:00
|
|
|
bool got_default = false;
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
|
2016-03-14 07:03:28 -05:00
|
|
|
if ((*it)->compare.size() == 0) {
|
|
|
|
if (got_default)
|
|
|
|
continue;
|
|
|
|
f << stringf("%s default", indent.c_str());
|
|
|
|
got_default = true;
|
|
|
|
} else {
|
|
|
|
f << stringf("%s ", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
for (size_t i = 0; i < (*it)->compare.size(); i++) {
|
|
|
|
if (i > 0)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(", ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, (*it)->compare[i]);
|
|
|
|
}
|
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(":\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_case_body(f, indent + " ", *it);
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "endcase\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void case_body_find_regs(RTLIL::CaseRule *cs)
|
|
|
|
{
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
|
|
|
|
case_body_find_regs(*it2);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
|
2014-07-25 07:23:31 -05:00
|
|
|
for (auto &c : it->first.chunks())
|
|
|
|
if (c.wire != NULL)
|
|
|
|
reg_wires.insert(c.wire->name);
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
if (find_regs) {
|
|
|
|
case_body_find_regs(&proc->root_case);
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
|
2014-07-25 07:23:31 -05:00
|
|
|
for (auto &c : it2->first.chunks())
|
|
|
|
if (c.wire != NULL)
|
|
|
|
reg_wires.insert(c.wire->name);
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "always @* begin\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_case_body(f, indent, &proc->root_case, true);
|
|
|
|
|
|
|
|
std::string backup_indent = indent;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < proc->syncs.size(); i++)
|
|
|
|
{
|
|
|
|
RTLIL::SyncRule *sync = proc->syncs[i];
|
|
|
|
indent = backup_indent;
|
|
|
|
|
|
|
|
if (sync->type == RTLIL::STa) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "always @* begin\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
} else {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "always @(", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("posedge ");
|
2013-01-05 04:13:26 -06:00
|
|
|
if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("negedge ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sync->signal);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(") begin\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
std::string ends = indent + "end\n";
|
|
|
|
indent += " ";
|
|
|
|
|
|
|
|
if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : "");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sync->signal);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(") begin\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
ends = indent + "end\n" + ends;
|
|
|
|
indent += " ";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) {
|
|
|
|
for (size_t j = 0; j < proc->syncs.size(); j++) {
|
|
|
|
RTLIL::SyncRule *sync2 = proc->syncs[j];
|
|
|
|
if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) {
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : "");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, sync2->signal);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(") begin\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
ends = indent + "end\n" + ends;
|
|
|
|
indent += " ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) {
|
2014-07-22 13:15:14 -05:00
|
|
|
if (it->first.size() == 0)
|
2013-01-05 04:13:26 -06:00
|
|
|
continue;
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s ", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, it->first);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(" <= ");
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_sigspec(f, it->second);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(";\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s", ends.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
reg_wires.clear();
|
|
|
|
reset_auto_counter(module);
|
|
|
|
active_module = module;
|
|
|
|
|
2016-03-14 07:03:28 -05:00
|
|
|
if (!module->processes.empty())
|
|
|
|
log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n"
|
|
|
|
"can't always be mapped directly to Verilog always blocks. Unintended\n"
|
|
|
|
"changes in simulation behavior are possible! Use \"proc\" to convert\n"
|
|
|
|
"processes to logic networks and registers.", log_id(module));
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("\n");
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_process(f, indent + " ", it->second, true);
|
|
|
|
|
|
|
|
if (!noexpr)
|
|
|
|
{
|
|
|
|
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
|
2014-07-26 18:51:45 -05:00
|
|
|
for (auto &it : module->cells_)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
RTLIL::Cell *cell = it.second;
|
2014-08-14 08:46:51 -05:00
|
|
|
if (!reg_ct.count(cell->type) || !cell->hasPort("\\Q"))
|
2013-01-05 04:13:26 -06:00
|
|
|
continue;
|
|
|
|
|
2014-07-31 09:38:54 -05:00
|
|
|
RTLIL::SigSpec sig = cell->getPort("\\Q");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-07-25 07:23:31 -05:00
|
|
|
if (sig.is_chunk()) {
|
|
|
|
RTLIL::SigChunk chunk = sig.as_chunk();
|
|
|
|
if (chunk.wire != NULL)
|
|
|
|
for (int i = 0; i < chunk.width; i++)
|
|
|
|
reg_bits.insert(std::pair<RTLIL::Wire*,int>(chunk.wire, chunk.offset+i));
|
|
|
|
}
|
2013-01-05 04:13:26 -06:00
|
|
|
}
|
2014-07-26 18:49:51 -05:00
|
|
|
for (auto &it : module->wires_)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
|
|
|
RTLIL::Wire *wire = it.second;
|
|
|
|
for (int i = 0; i < wire->width; i++)
|
|
|
|
if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
|
|
|
|
goto this_wire_aint_reg;
|
2014-09-06 13:30:46 -05:00
|
|
|
if (wire->width)
|
|
|
|
reg_wires.insert(wire->name);
|
2013-01-05 04:13:26 -06:00
|
|
|
this_wire_aint_reg:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-02 15:46:20 -06:00
|
|
|
dump_attributes(f, indent, module->attributes, '\n', true);
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
bool keep_running = true;
|
|
|
|
for (int port_id = 1; keep_running; port_id++) {
|
|
|
|
keep_running = false;
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it) {
|
2013-01-05 04:13:26 -06:00
|
|
|
RTLIL::Wire *wire = it->second;
|
|
|
|
if (wire->port_id == port_id) {
|
|
|
|
if (port_id != 1)
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(", ");
|
|
|
|
f << stringf("%s", id(wire->name).c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
keep_running = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf(");\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_wire(f, indent + " ", it->second);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->memories.begin(); it != module->memories.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_memory(f, indent + " ", it->second);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_cell(f, indent + " ", it->second);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_process(f, indent + " ", it->second);
|
|
|
|
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = module->connections().begin(); it != module->connections().end(); ++it)
|
2013-01-05 04:13:26 -06:00
|
|
|
dump_conn(f, indent + " ", it->first, it->second);
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
f << stringf("%s" "endmodule\n", indent.c_str());
|
2013-01-05 04:13:26 -06:00
|
|
|
active_module = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VerilogBackend : public Backend {
|
2015-08-14 04:27:19 -05:00
|
|
|
VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
|
2013-02-28 17:36:19 -06:00
|
|
|
virtual void help()
|
|
|
|
{
|
|
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
|
|
log("\n");
|
|
|
|
log(" write_verilog [options] [filename]\n");
|
|
|
|
log("\n");
|
2015-08-14 04:27:19 -05:00
|
|
|
log("Write the current design to a Verilog file.\n");
|
2013-02-28 17:36:19 -06:00
|
|
|
log("\n");
|
|
|
|
log(" -norename\n");
|
|
|
|
log(" without this option all internal object names (the ones with a dollar\n");
|
|
|
|
log(" instead of a backslash prefix) are changed to short names in the\n");
|
|
|
|
log(" format '_<number>_'.\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -noattr\n");
|
|
|
|
log(" with this option no attributes are included in the output\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -attr2comment\n");
|
|
|
|
log(" with this option attributes are included as comments in the output\n");
|
|
|
|
log("\n");
|
|
|
|
log(" -noexpr\n");
|
2015-08-14 04:27:19 -05:00
|
|
|
log(" without this option all internal cells are converted to Verilog\n");
|
2013-02-28 17:36:19 -06:00
|
|
|
log(" expressions.\n");
|
|
|
|
log("\n");
|
2013-11-22 08:01:12 -06:00
|
|
|
log(" -blackboxes\n");
|
|
|
|
log(" usually modules with the 'blackbox' attribute are ignored. with\n");
|
|
|
|
log(" this option set only the modules with the 'blackbox' attribute\n");
|
2013-03-28 03:20:10 -05:00
|
|
|
log(" are written to the output file.\n");
|
|
|
|
log("\n");
|
2013-09-03 12:10:11 -05:00
|
|
|
log(" -selected\n");
|
|
|
|
log(" only write selected modules. modules must be selected entirely or\n");
|
|
|
|
log(" not at all.\n");
|
|
|
|
log("\n");
|
2016-03-14 07:03:28 -05:00
|
|
|
log("Note that RTLIL processes can't always be mapped directly to Verilog\n");
|
|
|
|
log("always blocks. This frontend should only be used to export an RTLIL\n");
|
|
|
|
log("netlist, i.e. after the \"proc\" pass has been used to convert all\n");
|
|
|
|
log("processes to logic networks and registers. A warning is generated when\n");
|
|
|
|
log("this command is called on a design with RTLIL processes.\n");
|
|
|
|
log("\n");
|
2013-02-28 17:36:19 -06:00
|
|
|
}
|
2014-08-23 06:54:21 -05:00
|
|
|
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
2013-01-05 04:13:26 -06:00
|
|
|
{
|
2016-04-21 16:28:37 -05:00
|
|
|
log_header(design, "Executing Verilog backend.\n");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
norename = false;
|
|
|
|
noattr = false;
|
|
|
|
attr2comment = false;
|
|
|
|
noexpr = false;
|
|
|
|
|
2013-11-22 08:01:12 -06:00
|
|
|
bool blackboxes = false;
|
2013-09-03 12:10:11 -05:00
|
|
|
bool selected = false;
|
2013-03-28 03:20:10 -05:00
|
|
|
|
2013-01-05 04:13:26 -06:00
|
|
|
reg_ct.clear();
|
2014-08-14 08:46:51 -05:00
|
|
|
|
|
|
|
reg_ct.insert("$dff");
|
|
|
|
reg_ct.insert("$adff");
|
|
|
|
|
|
|
|
reg_ct.insert("$_DFF_N_");
|
|
|
|
reg_ct.insert("$_DFF_P_");
|
|
|
|
|
|
|
|
reg_ct.insert("$_DFF_NN0_");
|
|
|
|
reg_ct.insert("$_DFF_NN1_");
|
|
|
|
reg_ct.insert("$_DFF_NP0_");
|
|
|
|
reg_ct.insert("$_DFF_NP1_");
|
|
|
|
reg_ct.insert("$_DFF_PN0_");
|
|
|
|
reg_ct.insert("$_DFF_PN1_");
|
|
|
|
reg_ct.insert("$_DFF_PP0_");
|
|
|
|
reg_ct.insert("$_DFF_PP1_");
|
|
|
|
|
|
|
|
reg_ct.insert("$_DFFSR_NNN_");
|
|
|
|
reg_ct.insert("$_DFFSR_NNP_");
|
|
|
|
reg_ct.insert("$_DFFSR_NPN_");
|
|
|
|
reg_ct.insert("$_DFFSR_NPP_");
|
|
|
|
reg_ct.insert("$_DFFSR_PNN_");
|
|
|
|
reg_ct.insert("$_DFFSR_PNP_");
|
|
|
|
reg_ct.insert("$_DFFSR_PPN_");
|
|
|
|
reg_ct.insert("$_DFFSR_PPP_");
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
size_t argidx;
|
|
|
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
|
|
|
std::string arg = args[argidx];
|
|
|
|
if (arg == "-norename") {
|
|
|
|
norename = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (arg == "-noattr") {
|
|
|
|
noattr = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (arg == "-attr2comment") {
|
|
|
|
attr2comment = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (arg == "-noexpr") {
|
|
|
|
noexpr = true;
|
|
|
|
continue;
|
|
|
|
}
|
2013-11-22 08:01:12 -06:00
|
|
|
if (arg == "-blackboxes") {
|
|
|
|
blackboxes = true;
|
2013-03-28 03:20:10 -05:00
|
|
|
continue;
|
|
|
|
}
|
2013-09-03 12:10:11 -05:00
|
|
|
if (arg == "-selected") {
|
|
|
|
selected = true;
|
|
|
|
continue;
|
|
|
|
}
|
2013-01-05 04:13:26 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
extra_args(f, filename, args, argidx);
|
|
|
|
|
2015-01-23 17:13:27 -06:00
|
|
|
design->sort();
|
|
|
|
|
2014-08-23 06:54:21 -05:00
|
|
|
*f << stringf("/* Generated by %s */\n", yosys_version_str);
|
2014-12-26 14:35:22 -06:00
|
|
|
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
|
2013-11-22 08:01:12 -06:00
|
|
|
if (it->second->get_bool_attribute("\\blackbox") != blackboxes)
|
2013-09-03 12:10:11 -05:00
|
|
|
continue;
|
|
|
|
if (selected && !design->selected_whole_module(it->first)) {
|
|
|
|
if (design->selected_module(it->first))
|
|
|
|
log_cmd_error("Can't handle partially selected module %s!\n", RTLIL::id2cstr(it->first));
|
|
|
|
continue;
|
2013-03-28 03:20:10 -05:00
|
|
|
}
|
2013-09-03 12:10:11 -05:00
|
|
|
log("Dumping module `%s'.\n", it->first.c_str());
|
2014-08-23 06:54:21 -05:00
|
|
|
dump_module(*f, "", it->second);
|
2013-09-03 12:10:11 -05:00
|
|
|
}
|
2013-01-05 04:13:26 -06:00
|
|
|
|
|
|
|
reg_ct.clear();
|
|
|
|
}
|
|
|
|
} VerilogBackend;
|
|
|
|
|
2014-09-27 09:17:53 -05:00
|
|
|
PRIVATE_NAMESPACE_END
|