mirror of https://github.com/YosysHQ/yosys.git
486 lines
14 KiB
C++
486 lines
14 KiB
C++
/*
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
*
|
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* ---
|
|
*
|
|
* A very simple and straightforward backend for the RTLIL text
|
|
* representation (as understood by the 'ilang' frontend).
|
|
*
|
|
*/
|
|
|
|
#include "ilang_backend.h"
|
|
#include "kernel/compatibility.h"
|
|
#include "kernel/register.h"
|
|
#include "kernel/log.h"
|
|
#include <string>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
using namespace ILANG_BACKEND;
|
|
|
|
void ILANG_BACKEND::dump_const(FILE *f, const RTLIL::Const &data, int width, int offset, bool autoint)
|
|
{
|
|
if (width < 0)
|
|
width = data.bits.size() - offset;
|
|
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
|
if (width == 32 && autoint) {
|
|
int32_t val = 0;
|
|
for (int i = 0; i < width; i++) {
|
|
assert(offset+i < (int)data.bits.size());
|
|
switch (data.bits[offset+i]) {
|
|
case RTLIL::S0: break;
|
|
case RTLIL::S1: val |= 1 << i; break;
|
|
default: val = -1; break;
|
|
}
|
|
}
|
|
if (val >= 0) {
|
|
fprintf(f, "%d", val);
|
|
return;
|
|
}
|
|
}
|
|
fprintf(f, "%d'", width);
|
|
for (int i = offset+width-1; i >= offset; i--) {
|
|
assert(i < (int)data.bits.size());
|
|
switch (data.bits[i]) {
|
|
case RTLIL::S0: fprintf(f, "0"); break;
|
|
case RTLIL::S1: fprintf(f, "1"); break;
|
|
case RTLIL::Sx: fprintf(f, "x"); break;
|
|
case RTLIL::Sz: fprintf(f, "z"); break;
|
|
case RTLIL::Sa: fprintf(f, "-"); break;
|
|
case RTLIL::Sm: fprintf(f, "m"); break;
|
|
}
|
|
}
|
|
} else {
|
|
fprintf(f, "\"");
|
|
std::string str = data.decode_string();
|
|
for (size_t i = 0; i < str.size(); i++) {
|
|
if (str[i] == '\n')
|
|
fprintf(f, "\\n");
|
|
else if (str[i] == '\t')
|
|
fprintf(f, "\\t");
|
|
else if (str[i] < 32)
|
|
fprintf(f, "\\%03o", str[i]);
|
|
else if (str[i] == '"')
|
|
fprintf(f, "\\\"");
|
|
else if (str[i] == '\\')
|
|
fprintf(f, "\\\\");
|
|
else
|
|
fputc(str[i], f);
|
|
}
|
|
fprintf(f, "\"");
|
|
}
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint)
|
|
{
|
|
if (chunk.wire == NULL) {
|
|
dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
|
|
} else {
|
|
if (chunk.width == chunk.wire->width && chunk.offset == 0)
|
|
fprintf(f, "%s", chunk.wire->name.c_str());
|
|
else if (chunk.width == 1)
|
|
fprintf(f, "%s [%d]", chunk.wire->name.c_str(), chunk.offset);
|
|
else
|
|
fprintf(f, "%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset);
|
|
}
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint)
|
|
{
|
|
if (sig.__chunks.size() == 1) {
|
|
dump_sigchunk(f, sig.__chunks[0], autoint);
|
|
} else {
|
|
fprintf(f, "{ ");
|
|
for (auto it = sig.__chunks.rbegin(); it != sig.__chunks.rend(); it++) {
|
|
dump_sigchunk(f, *it, false);
|
|
fprintf(f, " ");
|
|
}
|
|
fprintf(f, "}");
|
|
}
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire)
|
|
{
|
|
for (auto it = wire->attributes.begin(); it != wire->attributes.end(); it++) {
|
|
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
fprintf(f, "%s" "wire ", indent.c_str());
|
|
if (wire->width != 1)
|
|
fprintf(f, "width %d ", wire->width);
|
|
if (wire->start_offset != 0)
|
|
fprintf(f, "offset %d ", wire->start_offset);
|
|
if (wire->port_input && !wire->port_output)
|
|
fprintf(f, "input %d ", wire->port_id);
|
|
if (!wire->port_input && wire->port_output)
|
|
fprintf(f, "output %d ", wire->port_id);
|
|
if (wire->port_input && wire->port_output)
|
|
fprintf(f, "inout %d ", wire->port_id);
|
|
fprintf(f, "%s\n", wire->name.c_str());
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory)
|
|
{
|
|
for (auto it = memory->attributes.begin(); it != memory->attributes.end(); it++) {
|
|
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
fprintf(f, "%s" "memory ", indent.c_str());
|
|
if (memory->width != 1)
|
|
fprintf(f, "width %d ", memory->width);
|
|
if (memory->size != 0)
|
|
fprintf(f, "size %d ", memory->size);
|
|
fprintf(f, "%s\n", memory->name.c_str());
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell)
|
|
{
|
|
for (auto it = cell->attributes.begin(); it != cell->attributes.end(); it++) {
|
|
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
fprintf(f, "%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
|
|
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) {
|
|
fprintf(f, "%s parameter%s %s ", indent.c_str(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
|
|
fprintf(f, "%s connect %s ", indent.c_str(), it->first.c_str());
|
|
dump_sigspec(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
fprintf(f, "%s" "end\n", indent.c_str());
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs)
|
|
{
|
|
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
|
|
{
|
|
fprintf(f, "%s" "assign ", indent.c_str());
|
|
dump_sigspec(f, it->first);
|
|
fprintf(f, " ");
|
|
dump_sigspec(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
|
|
dump_proc_switch(f, indent, *it);
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw)
|
|
{
|
|
for (auto it = sw->attributes.begin(); it != sw->attributes.end(); it++) {
|
|
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
fprintf(f, "%s" "switch ", indent.c_str());
|
|
dump_sigspec(f, sw->signal);
|
|
fprintf(f, "\n");
|
|
|
|
for (auto it = sw->cases.begin(); it != sw->cases.end(); it++)
|
|
{
|
|
fprintf(f, "%s case ", indent.c_str());
|
|
for (size_t i = 0; i < (*it)->compare.size(); i++) {
|
|
if (i > 0)
|
|
fprintf(f, ", ");
|
|
dump_sigspec(f, (*it)->compare[i]);
|
|
}
|
|
fprintf(f, "\n");
|
|
|
|
dump_proc_case_body(f, indent + " ", *it);
|
|
}
|
|
|
|
fprintf(f, "%s" "end\n", indent.c_str());
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy)
|
|
{
|
|
fprintf(f, "%s" "sync ", indent.c_str());
|
|
switch (sy->type) {
|
|
if (0) case RTLIL::ST0: fprintf(f, "low ");
|
|
if (0) case RTLIL::ST1: fprintf(f, "high ");
|
|
if (0) case RTLIL::STp: fprintf(f, "posedge ");
|
|
if (0) case RTLIL::STn: fprintf(f, "negedge ");
|
|
if (0) case RTLIL::STe: fprintf(f, "edge ");
|
|
dump_sigspec(f, sy->signal);
|
|
fprintf(f, "\n");
|
|
break;
|
|
case RTLIL::STa: fprintf(f, "always\n"); break;
|
|
case RTLIL::STi: fprintf(f, "init\n"); break;
|
|
}
|
|
|
|
for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) {
|
|
fprintf(f, "%s update ", indent.c_str());
|
|
dump_sigspec(f, it->first);
|
|
fprintf(f, " ");
|
|
dump_sigspec(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc)
|
|
{
|
|
for (auto it = proc->attributes.begin(); it != proc->attributes.end(); it++) {
|
|
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
fprintf(f, "%s" "process %s\n", indent.c_str(), proc->name.c_str());
|
|
dump_proc_case_body(f, indent + " ", &proc->root_case);
|
|
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++)
|
|
dump_proc_sync(f, indent + " ", *it);
|
|
fprintf(f, "%s" "end\n", indent.c_str());
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
|
|
{
|
|
fprintf(f, "%s" "connect ", indent.c_str());
|
|
dump_sigspec(f, left);
|
|
fprintf(f, " ");
|
|
dump_sigspec(f, right);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_module(FILE *f, std::string indent, const RTLIL::Module *module, const RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
|
|
{
|
|
bool print_header = flag_m || design->selected_whole_module(module->name);
|
|
bool print_body = !flag_n || !design->selected_whole_module(module->name);
|
|
|
|
if (print_header)
|
|
{
|
|
for (auto it = module->attributes.begin(); it != module->attributes.end(); it++) {
|
|
fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
|
dump_const(f, it->second);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
fprintf(f, "%s" "module %s\n", indent.c_str(), module->name.c_str());
|
|
}
|
|
|
|
if (print_body)
|
|
{
|
|
for (auto it = module->wires.begin(); it != module->wires.end(); it++)
|
|
if (!only_selected || design->selected(module, it->second)) {
|
|
if (only_selected)
|
|
fprintf(f, "\n");
|
|
dump_wire(f, indent + " ", it->second);
|
|
}
|
|
|
|
for (auto it = module->memories.begin(); it != module->memories.end(); it++)
|
|
if (!only_selected || design->selected(module, it->second)) {
|
|
if (only_selected)
|
|
fprintf(f, "\n");
|
|
dump_memory(f, indent + " ", it->second);
|
|
}
|
|
|
|
for (auto it = module->cells.begin(); it != module->cells.end(); it++)
|
|
if (!only_selected || design->selected(module, it->second)) {
|
|
if (only_selected)
|
|
fprintf(f, "\n");
|
|
dump_cell(f, indent + " ", it->second);
|
|
}
|
|
|
|
for (auto it = module->processes.begin(); it != module->processes.end(); it++)
|
|
if (!only_selected || design->selected(module, it->second)) {
|
|
if (only_selected)
|
|
fprintf(f, "\n");
|
|
dump_proc(f, indent + " ", it->second);
|
|
}
|
|
|
|
bool first_conn_line = true;
|
|
for (auto it = module->connections.begin(); it != module->connections.end(); it++) {
|
|
bool show_conn = !only_selected;
|
|
if (only_selected) {
|
|
RTLIL::SigSpec sigs = it->first;
|
|
sigs.append(it->second);
|
|
for (auto &c : sigs.__chunks) {
|
|
if (c.wire == NULL || !design->selected(module, c.wire))
|
|
continue;
|
|
show_conn = true;
|
|
}
|
|
}
|
|
if (show_conn) {
|
|
if (only_selected && first_conn_line)
|
|
fprintf(f, "\n");
|
|
dump_conn(f, indent + " ", it->first, it->second);
|
|
first_conn_line = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (print_header)
|
|
fprintf(f, "%s" "end\n", indent.c_str());
|
|
}
|
|
|
|
void ILANG_BACKEND::dump_design(FILE *f, const RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
|
|
{
|
|
int init_autoidx = RTLIL::autoidx;
|
|
|
|
if (!flag_m) {
|
|
int count_selected_mods = 0;
|
|
for (auto it = design->modules.begin(); it != design->modules.end(); it++) {
|
|
if (design->selected_whole_module(it->first))
|
|
flag_m = true;
|
|
if (design->selected(it->second))
|
|
count_selected_mods++;
|
|
}
|
|
if (count_selected_mods > 1)
|
|
flag_m = true;
|
|
}
|
|
|
|
if (!only_selected || flag_m) {
|
|
if (only_selected)
|
|
fprintf(f, "\n");
|
|
fprintf(f, "autoidx %d\n", RTLIL::autoidx);
|
|
}
|
|
|
|
for (auto it = design->modules.begin(); it != design->modules.end(); it++) {
|
|
if (!only_selected || design->selected(it->second)) {
|
|
if (only_selected)
|
|
fprintf(f, "\n");
|
|
dump_module(f, "", it->second, design, only_selected, flag_m, flag_n);
|
|
}
|
|
}
|
|
|
|
log_assert(init_autoidx == RTLIL::autoidx);
|
|
}
|
|
|
|
struct IlangBackend : public Backend {
|
|
IlangBackend() : Backend("ilang", "write design to ilang file") { }
|
|
virtual void help()
|
|
{
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
log("\n");
|
|
log(" write_ilang [filename]\n");
|
|
log("\n");
|
|
log("Write the current design to an 'ilang' file. (ilang is a text representation\n");
|
|
log("of a design in yosys's internal format.)\n");
|
|
log("\n");
|
|
log(" -selected\n");
|
|
log(" only write selected parts of the design.\n");
|
|
log("\n");
|
|
}
|
|
virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
|
{
|
|
bool selected = false;
|
|
|
|
log_header("Executing ILANG backend.\n");
|
|
|
|
size_t argidx;
|
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
|
std::string arg = args[argidx];
|
|
if (arg == "-selected") {
|
|
selected = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
extra_args(f, filename, args, argidx);
|
|
|
|
log("Output filename: %s\n", filename.c_str());
|
|
fprintf(f, "# Generated by %s\n", yosys_version_str);
|
|
ILANG_BACKEND::dump_design(f, design, selected, true, false);
|
|
}
|
|
} IlangBackend;
|
|
|
|
struct DumpPass : public Pass {
|
|
DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
|
|
virtual void help()
|
|
{
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
log("\n");
|
|
log(" dump [options] [selection]\n");
|
|
log("\n");
|
|
log("Write the selected parts of the design to the console or specified file in\n");
|
|
log("ilang format.\n");
|
|
log("\n");
|
|
log(" -m\n");
|
|
log(" also dump the module headers, even if only parts of a single\n");
|
|
log(" module is selected\n");
|
|
log("\n");
|
|
log(" -n\n");
|
|
log(" only dump the module headers if the entire module is selected\n");
|
|
log("\n");
|
|
log(" -outfile <filename>\n");
|
|
log(" write to the specified file.\n");
|
|
log("\n");
|
|
log(" -append <filename>\n");
|
|
log(" like -outfile but append instead of overwrite\n");
|
|
log("\n");
|
|
}
|
|
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
|
{
|
|
std::string filename;
|
|
bool flag_m = false, flag_n = false, append = false;
|
|
|
|
size_t argidx;
|
|
for (argidx = 1; argidx < args.size(); argidx++)
|
|
{
|
|
std::string arg = args[argidx];
|
|
if (arg == "-outfile" && argidx+1 < args.size()) {
|
|
filename = args[++argidx];
|
|
append = false;
|
|
continue;
|
|
}
|
|
if (arg == "-append" && argidx+1 < args.size()) {
|
|
filename = args[++argidx];
|
|
append = true;
|
|
continue;
|
|
}
|
|
if (arg == "-m") {
|
|
flag_m = true;
|
|
continue;
|
|
}
|
|
if (arg == "-n") {
|
|
flag_n = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
extra_args(args, argidx, design);
|
|
|
|
FILE *f = NULL;
|
|
char *buf_ptr;
|
|
size_t buf_size;
|
|
|
|
if (!filename.empty()) {
|
|
f = fopen(filename.c_str(), append ? "a" : "w");
|
|
if (f == NULL)
|
|
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
|
} else {
|
|
f = open_memstream(&buf_ptr, &buf_size);
|
|
}
|
|
|
|
ILANG_BACKEND::dump_design(f, design, true, flag_m, flag_n);
|
|
|
|
fclose(f);
|
|
|
|
if (filename.empty()) {
|
|
log("%s", buf_ptr);
|
|
free(buf_ptr);
|
|
}
|
|
}
|
|
} DumpPass;
|
|
|