/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * 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 simple and straightforward Verilog backend. * */ #include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/sigtools.h" #include "kernel/ff.h" #include "kernel/mem.h" #include #include #include #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; std::set reg_wires; std::string auto_prefix, extmem_prefix; RTLIL::Module *active_module; dict active_initdata; SigMap active_sigmap; IdString initial_id; void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { const char *str = id.c_str(); if (*str == '$' && may_rename && !norename) auto_name_map[id] = auto_name_counter++; if (str[0] != '\\' || str[1] != '_' || str[2] == 0) return; for (int i = 2; str[i] != 0; i++) { if (str[i] == '_' && str[i+1] == 0) continue; if (str[i] < '0' || str[i] > '9') return; } int num = atoi(str+2); 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); for (auto w : module->wires()) reset_auto_counter_id(w->name, true); for (auto cell : module->cells()) { reset_auto_counter_id(cell->name, true); reset_auto_counter_id(cell->type, false); } for (auto it = module->processes.begin(); it != module->processes.end(); ++it) 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++; if (verbose) for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it) log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second); } std::string next_auto_id() { return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++); } std::string id(RTLIL::IdString internal_id, bool may_rename = true) { const char *str = internal_id.c_str(); bool do_escape = false; if (may_rename && auto_name_map.count(internal_id) != 0) return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]); 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; } const pool keywords = { // IEEE 1800-2017 Annex B "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", "wildcard", "wire", "with", "within", "wor", "xnor", "xor", }; if (keywords.count(str)) do_escape = true; if (do_escape) return "\\" + std::string(str) + " "; return std::string(str); } bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) { if (!sig.is_chunk() || sig.as_chunk().wire == NULL) return false; RTLIL::SigChunk chunk = sig.as_chunk(); if (reg_wires.count(chunk.wire->name) == 0) return false; reg_name = id(chunk.wire->name); if (sig.size() != chunk.wire->width) { if (sig.size() == 1) reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset); else if (chunk.wire->upto) reg_name += stringf("[%d:%d]", (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1, chunk.wire->start_offset + chunk.offset); } return true; } void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false) { bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; if (width < 0) width = data.bits.size() - offset; if (width == 0) { // See IEEE 1364-2005 Clause 5.1.14. f << "{0{1'b0}}"; return; } if (nostr) goto dump_hex; if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) { if (width == 32 && !no_decimal && !nodec) { int32_t val = 0; for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); if (data.bits[i] != State::S0 && data.bits[i] != State::S1) goto dump_hex; if (data.bits[i] == State::S1) val |= 1 << (i - offset); } if (decimal) f << stringf("%d", val); else if (set_signed && val < 0) f << stringf("-32'sd%u", -val); else f << stringf("32'%sd%u", set_signed ? "s" : "", val); } else { dump_hex: if (nohex) goto dump_bin; vector bin_digits, hex_digits; for (int i = offset; i < offset+width; i++) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { case State::S0: bin_digits.push_back('0'); break; case State::S1: bin_digits.push_back('1'); break; case RTLIL::Sx: bin_digits.push_back('x'); break; case RTLIL::Sz: bin_digits.push_back('z'); break; case RTLIL::Sa: bin_digits.push_back('?'); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } if (GetSize(bin_digits) == 0) goto dump_bin; while (GetSize(bin_digits) % 4 != 0) if (bin_digits.back() == '1') bin_digits.push_back('0'); else bin_digits.push_back(bin_digits.back()); for (int i = 0; i < GetSize(bin_digits); i += 4) { char bit_3 = bin_digits[i+3]; char bit_2 = bin_digits[i+2]; char bit_1 = bin_digits[i+1]; char bit_0 = bin_digits[i+0]; if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') { if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x') goto dump_bin; hex_digits.push_back('x'); continue; } if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') { if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z') goto dump_bin; hex_digits.push_back('z'); continue; } if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') { if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?') goto dump_bin; hex_digits.push_back('?'); continue; } int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); } f << stringf("%d'%sh", width, set_signed ? "s" : ""); for (int i = GetSize(hex_digits)-1; i >= 0; i--) f << hex_digits[i]; } if (0) { dump_bin: f << stringf("%d'%sb", width, set_signed ? "s" : ""); if (width == 0) f << stringf("0"); for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { case State::S0: f << stringf("0"); break; case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sz: f << stringf("z"); break; case RTLIL::Sa: f << stringf("?"); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } } } else { if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) f << stringf("\""); std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { if (str[i] == '\n') f << stringf("\\n"); else if (str[i] == '\t') f << stringf("\\t"); else if (str[i] < 32) f << stringf("\\%03o", str[i]); else if (str[i] == '"') f << stringf("\\\""); else if (str[i] == '\\') f << stringf("\\\\"); else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*') f << stringf("\\/"); else f << str[i]; } if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) f << stringf("\""); } } void dump_reg_init(std::ostream &f, SigSpec sig) { Const initval; bool gotinit = false; for (auto bit : active_sigmap(sig)) { if (active_initdata.count(bit)) { initval.bits.push_back(active_initdata.at(bit)); gotinit = true; } else { initval.bits.push_back(State::Sx); } } if (gotinit) { f << " = "; dump_const(f, initval); } } void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false) { if (chunk.wire == NULL) { dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); } else { if (chunk.width == chunk.wire->width && chunk.offset == 0) { f << stringf("%s", id(chunk.wire->name).c_str()); } else if (chunk.width == 1) { if (chunk.wire->upto) f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); } else { if (chunk.wire->upto) f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), (chunk.offset + chunk.width - 1) + chunk.wire->start_offset, chunk.offset + chunk.wire->start_offset); } } } void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) { if (GetSize(sig) == 0) { // See IEEE 1364-2005 Clause 5.1.14. f << "{0{1'b0}}"; return; } if (sig.is_chunk()) { dump_sigchunk(f, sig.as_chunk()); } else { f << stringf("{ "); for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { if (it != sig.chunks().rbegin()) f << stringf(", "); dump_sigchunk(f, *it, true); } f << stringf(" }"); } } void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false) { if (noattr) return; if (attr2comment) as_comment = true; for (auto it = attributes.begin(); it != attributes.end(); ++it) { if (it->first == ID::init && regattr) continue; f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str()); f << stringf(" = "); if (modattr && (it->second == State::S0 || it->second == Const(0))) f << stringf(" 0 "); else if (modattr && (it->second == State::S1 || it->second == Const(1))) f << stringf(" 1 "); else dump_const(f, it->second, -1, 0, false, as_comment); f << stringf(" %s%c", as_comment ? "*/" : "*)", term); } } void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) { dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); #if 0 if (wire->port_input && !wire->port_output) f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); else if (!wire->port_input && wire->port_output) f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); else if (wire->port_input && wire->port_output) f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); else f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire"); if (wire->width != 1) f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset); f << stringf("%s;\n", id(wire->name).c_str()); #else // do not use Verilog-2k "output reg" syntax in Verilog export std::string range = ""; 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); } if (wire->port_input && !wire->port_output) f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (!wire->port_input && wire->port_output) f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (wire->port_input && wire->port_output) f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (reg_wires.count(wire->name)) { f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (wire->attributes.count(ID::init)) { f << stringf(" = "); dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); } else f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); #endif } void dump_memory(std::ostream &f, std::string indent, Mem &mem) { std::string mem_id = id(mem.memid); dump_attributes(f, indent, mem.attributes); f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), mem.width-1, mem_id.c_str(), mem.size+mem.start_offset-1, mem.start_offset); // for memory block make something like: // reg [7:0] memid [3:0]; // initial begin // memid[0] = ... // end if (!mem.inits.empty()) { if (extmem) { std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++); std::string extmem_filename_esc; for (auto c : extmem_filename) { if (c == '\n') extmem_filename_esc += "\\n"; else if (c == '\t') extmem_filename_esc += "\\t"; else if (c < 32) extmem_filename_esc += stringf("\\%03o",
"""
module pyabc_split

Executes python functions and their arguements as separate processes and returns their return values through pickling. This modules offers a single function:

Function: split_all(funcs)

The function returns a generator objects that allowes iteration over the results,

Arguments:

funcs: a list of tuples (f, args) where f is a python function and args is a collection of arguments for f.

Caveats:

1. Global variables in the parent process are not affected by the child processes.
2. The functions can only return simple types, see the pickle module for details

Usage:

Assume you would like to run the function f_1(1), f_2(1,2), f_3(1,2,3) in different processes. 

def f_1(i):
    return i+1
    
def f_2(i,j):
    return i*10+j+1
    
def f_3(i,j,k):
    return i*100+j*10+k+1

Construct a tuple of the function and arguments for each function

t_1 = (f_1, [1])
t_2 = (f_2, [1,2])
t_3 = (f_3, [1,2,3])

Create a list containing these tuples:

funcs = [t_1, t_2, t_3]

Use the function split_all() to run these functions in separate processes:

for res in split_all(funcs):
    print res
    
The output will be:

2
13
124

(The order may be different, except that in this case the processes are so fast that they terminate before the next one is created)

Alternatively, you may quite in the middle, say after the first process returns:

for res in split_all(funcs):
    print res
    break
    
This will kill all processes not yet finished.

To run ABC operations, that required saving the child process state and restoring it at the parent, use abc_split_all().

    import pyabc
    
    def abc_f(truth):
        import os
        print "pid=%d, abc_f(%s)"%(os.getpid(), truth)
        pyabc.run_command('read_truth %s'%truth)
        pyabc.run_command('strash')
        
    funcs = [
        defer(abc_f)("1000"),
        defer(abc_f)("0001")
    ]
    
    for _ in abc_split_all(funcs):
        pyabc.run_command('write_verilog /dev/stdout')

Author: Baruch Sterin <sterin@berkeley.edu>
"""

import os
import select
import fcntl
import errno
import sys
import cPickle as pickle
import signal
import cStringIO

import traceback

from contextlib import contextmanager

import pyabc

def _retry_select(rlist):
    while True:
        try:
            rrdy,_,_ = select.select(rlist,[],[])
            if rrdy:
                return rrdy
        except select.error as e:
            if e[0] == errno.EINTR:
                continue
            raise

class _splitter(object):
    
    def __init__(self):
        self.pids = []
        self.fds = {}
        self.buffers = {}
        self.results = {}
    
    def is_done(self):
        return len(self.fds) == 0
    
    def _kill(self, pids):

        # close pipes and kill child processes
        for pid in pids:

            if pid == -1:
                continue

            i, fd = self.fds[pid]
            
            del self.buffers[fd]
            del self.fds[pid]

            self.pids[i] = -1
            self.results[pid] = None
            
            os.close(fd)
            
            try:
                os.kill( pid, signal.SIGINT)
            except Exception as e:
                print >>sys.stderr, 'exception while trying to kill pid=%d: '%pid, e
                raise

        # wait for termination and update result
        for pid in pids:
            os.waitpid( pid, 0 )

    def kill(self, ids):
        
        self._kill( [ self.pids[i] for i in ids ] )
            
    def cleanup(self):
        self._kill( self.fds.keys() )

    def child( self, fdw, f):

        # call function
        try:
            res = f()
        except:
            traceback.print_exc()
            raise
        
        # write return value into pipe
        with os.fdopen( fdw, "w" ) as fout:
            pickle.dump(res, fout)
            
        return 0

    def _fork_one(self, f):
        
        # create a pipe to communicate with the child process
        pr,pw = os.pipe()
        
        # set pr to be non-blocking
        fcntl.fcntl(pr, fcntl.F_SETFL, os.O_NONBLOCK)
        
        parentpid = os.getpid()
        rc = 1
        
        try:

            # create child process
            pid = os.fork()
    
            if pid == 0:
                # child process:
                os.close(pr)
                pyabc.close_on_fork(pw)
                
                rc = self.child( pw, f)
                os._exit(rc)
            else:
                # parent process:
                os.close(pw)
                return (pid, pr)
                
        finally:
            if os.getpid() != parentpid:
                os._exit(rc)

    def fork_one(self, func):
        pid, fd = self._fork_one(func)
        i = len(self.pids)
        self.pids.append(pid)
        self.fds[pid] = (i, fd)
        self.buffers[fd] = cStringIO.StringIO()
        return i
    
    def fork_all(self, funcs):
        return [ self.fork_one(f) for f in funcs ]

    def communicate(self):
        
        rlist = [ fd for _, (_,fd) in self.fds.iteritems() ]
        rlist.append(pyabc.wait_fd)

        stop = False
        
        while not stop:

            rrdy = _retry_select( rlist )
            
            for fd in rrdy:
                
                if fd == pyabc.wait_fd:
                    stop = True
                    continue
                    
                self.buffers[fd].write( os.read(fd, 16384) )
    
    def get_next_result(self):

        # read from the pipes as needed, while waiting for the next child process to terminate
        self.communicate()
    
        # wait for the next child process to terminate
        pid, rc = os.wait()
        assert pid in self.fds
        
        # retrieve the pipe file descriptor
        i, fd = self.fds[pid]
        del self.fds[pid]

        # remove the pid
        self.pids[i] = -1

        # retrieve the buffer
        buffer = self.buffers[fd]
        del self.buffers[fd]

        # fill the buffer
        while True:
            s = os.read(fd, 16384)
            if not s:
                break
            buffer.write(s)
            
        os.close(fd)

        try:
            return (i, pickle.loads(buffer.getvalue()))
        except EOFError, pickle.UnpicklingError:
            return (i, None)
            
    def __iter__(self):
        def iterator():
            while not self.is_done():
                yield self.get_next_result()
        return iterator()

@contextmanager
def make_splitter():
    # ensure cleanup of child processes
    s = _splitter()
    try:
        yield s
    finally:
        s.cleanup()
        
def split_all_full(funcs):
    # provide an iterator for child process result
    with make_splitter() as s:
        
        s.fork_all(funcs)
        
        for res in s:
            yield res

def defer(f):
    return lambda *args, **kwargs: lambda : f(*args,**kwargs)

def split_all(funcs):
    for _, res in split_all_full( ( defer(f)(*args) for f,args in funcs ) ):
        yield res

import tempfile

@contextmanager
def temp_file_names(suffixes):
    names = []
    try:
        for suffix in suffixes:
            with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as file:
                names.append( file.name )
        yield names
    finally:
        for name in names:
            os.unlink(name)

class abc_state(object):
    def __init__(self):
        with tempfile.NamedTemporaryFile(delete=False, suffix='.aig') as file:
            self.aig = file.name
        with tempfile.NamedTemporaryFile(delete=False, suffix='.log') as file:
            self.log = file.name
        pyabc.run_command(r'write_status %s'%self.log)
        pyabc.run_command(r'write_aiger %s'%self.aig)
    
    def __del__(self):
        os.unlink( self.aig )
        os.unlink( self.log )

    def restore(self):
        pyabc.run_command(r'read_aiger %s'%self.aig)
        pyabc.run_command(r'read_status %s'%self.log)

def abc_split_all(funcs):
    import pyabc
    
    def child(f, aig, log):
        res = f()
        pyabc.run_command(r'write_status %s'%log)
        pyabc.run_command(r'write_aiger %s'%aig)
        return res
        
    def parent(res, aig, log):
        pyabc.run_command(r'read_aiger %s'%aig)
        pyabc.run_command(r'read_status %s'%log)
        return res
        
    with temp_file_names( ['.aig','.log']*len(funcs) ) as tmp:

        funcs = [ defer(child)(f, tmp[2*i],tmp[2*i+1]) for i,f in enumerate(funcs) ]
        
        for i, res in split_all_full(funcs):
            yield i, parent(res, tmp[2*i],tmp[2*i+1])

if __name__ == "__main__":
    
    # define some functions to run
    
    def f_1(i):
        return i+1
        
    def f_2(i,j):
        return i*10+j+1
        
    def f_3(i,j,k):
        return i*100+j*10+k+1
    
    # Construct a tuple of the function and arguments for each function
    
    t_1 = (f_1, [1])
    t_2 = (f_2, [1,2])
    t_3 = (f_3, [1,2,3])

    # Create a list containing these tuples:

    funcs = [t_1, t_2, t_3]

    # Use the function split_all() to run these functions in separate processes:

    for res in split_all(funcs):
        print res

    # Alternatively, quit after the first process returns:
    
    for res in split_all(funcs):
        print res
        break
        
    # For operations with ABC that save and restore status
    
    import pyabc
    
    def abc_f(truth):
        import os
        print "pid=%d, abc_f(%s)"%(os.getpid(), truth)
        pyabc.run_command('read_truth %s'%truth)
        pyabc.run_command('strash')
        return 100
        
    funcs = [
        defer(abc_f)("1000"),
        defer(abc_f)("0001")
    ]
    
    best = None
    
    for i, res in abc_split_all(funcs):
        print i, res
        if best is None:\
            # save state
            best = abc_state()
        pyabc.run_command('write_verilog /dev/stdout')
    
    # if there is a saved state, restore it
    if best is not None:
        best.restore()
        pyabc.run_command('write_verilog /dev/stdout')
f (reg_bits.count(std::pair(wire, i)) == 0) goto this_wire_aint_reg; if (wire->width) reg_wires.insert(wire->name); this_wire_aint_reg:; } } dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; int cnt = 0; for (int port_id = 1; keep_running; port_id++) { keep_running = false; for (auto wire : module->wires()) { if (wire->port_id == port_id) { if (port_id != 1) f << stringf(", "); f << stringf("%s", id(wire->name).c_str()); keep_running = true; if (cnt==20) { f << stringf("\n"); cnt = 0; } else cnt++; continue; } } } f << stringf(");\n"); if (!systemverilog && !module->processes.empty()) { initial_id = NEW_ID; f << indent + " " << "reg " << id(initial_id) << " = 0;\n"; } for (auto w : module->wires()) dump_wire(f, indent + " ", w); for (auto &mem : Mem::get_all_memories(module)) dump_memory(f, indent + " ", mem); for (auto cell : module->cells()) dump_cell(f, indent + " ", cell); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) dump_process(f, indent + " ", it->second); for (auto it = module->connections().begin(); it != module->connections().end(); ++it) dump_conn(f, indent + " ", it->first, it->second); f << stringf("%s" "endmodule\n", indent.c_str()); active_module = NULL; active_sigmap.clear(); active_initdata.clear(); } struct VerilogBackend : public Backend { VerilogBackend() : Backend("verilog", "write design to Verilog file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_verilog [options] [filename]\n"); log("\n"); log("Write the current design to a Verilog file.\n"); log("\n"); log(" -sv\n"); log(" with this option, SystemVerilog constructs like always_comb are used\n"); 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 '__'.\n"); log("\n"); log(" -renameprefix \n"); log(" insert this prefix in front of auto-generated instance names\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"); log(" without this option all internal cells are converted to Verilog\n"); log(" expressions.\n"); log("\n"); log(" -siminit\n"); log(" add initial statements with hierarchical refs to initialize FFs when\n"); log(" in -noexpr mode.\n"); log("\n"); log(" -nodec\n"); log(" 32-bit constant values are by default dumped as decimal numbers,\n"); log(" not bit pattern. This option deactivates this feature and instead\n"); log(" will write out all constants in binary.\n"); log("\n"); log(" -decimal\n"); log(" dump 32-bit constants in decimal and without size and radix\n"); log("\n"); log(" -nohex\n"); log(" constant values that are compatible with hex output are usually\n"); log(" dumped as hex values. This option deactivates this feature and\n"); log(" instead will write out all constants in binary.\n"); log("\n"); log(" -nostr\n"); log(" Parameters and attributes that are specified as strings in the\n"); log(" original input will be output as strings by this back-end. This\n"); log(" deactivates this feature and instead will write string constants\n"); log(" as binary numbers.\n"); log("\n"); log(" -simple-lhs\n"); log(" Connection assignments with simple left hand side without concatenations.\n"); log("\n"); log(" -extmem\n"); log(" instead of initializing memories using assignments to individual\n"); log(" elements, use the '$readmemh' function to read initialization data\n"); log(" from a file. This data is written to a file named by appending\n"); log(" a sequential index to the Verilog filename and replacing the extension\n"); log(" with '.mem', e.g. 'write_verilog -extmem foo.v' writes 'foo-1.mem',\n"); log(" 'foo-2.mem' and so on.\n"); log("\n"); log(" -defparam\n"); log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n"); log(" cell parameters.\n"); log("\n"); 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"); log(" are written to the output file.\n"); log("\n"); log(" -selected\n"); log(" only write selected modules. modules must be selected entirely or\n"); log(" not at all.\n"); log("\n"); log(" -v\n"); log(" verbose output (print new names of all renamed wires and cells)\n"); log("\n"); 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"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing Verilog backend.\n"); verbose = false; norename = false; noattr = false; attr2comment = false; noexpr = false; nodec = false; nohex = false; nostr = false; extmem = false; defparam = false; decimal = false; siminit = false; simple_lhs = false; auto_prefix = ""; bool blackboxes = false; bool selected = false; auto_name_map.clear(); reg_wires.clear(); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-sv") { systemverilog = true; continue; } if (arg == "-norename") { norename = true; continue; } if (arg == "-renameprefix" && argidx+1 < args.size()) { auto_prefix = args[++argidx]; continue; } if (arg == "-noattr") { noattr = true; continue; } if (arg == "-attr2comment") { attr2comment = true; continue; } if (arg == "-noexpr") { noexpr = true; continue; } if (arg == "-nodec") { nodec = true; continue; } if (arg == "-nohex") { nohex = true; continue; } if (arg == "-nostr") { nostr = true; continue; } if (arg == "-extmem") { extmem = true; extmem_counter = 1; continue; } if (arg == "-defparam") { defparam = true; continue; } if (arg == "-decimal") { decimal = true; continue; } if (arg == "-siminit") { siminit = true; continue; } if (arg == "-blackboxes") { blackboxes = true; continue; } if (arg == "-selected") { selected = true; continue; } if (arg == "-simple-lhs") { simple_lhs = true; continue; } if (arg == "-v") { verbose = true; continue; } break; } extra_args(f, filename, args, argidx); if (extmem) { if (filename == "") log_cmd_error("Option -extmem must be used with a filename.\n"); extmem_prefix = filename.substr(0, filename.rfind('.')); } log_push(); Pass::call(design, "bmuxmap"); Pass::call(design, "demuxmap"); Pass::call(design, "clean_zerowidth"); log_pop(); design->sort(); *f << stringf("/* Generated by %s */\n", yosys_version_str); for (auto module : design->modules()) { if (module->get_blackbox_attribute() != blackboxes) continue; if (selected && !design->selected_whole_module(module->name)) { if (design->selected_module(module->name)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name)); continue; } log("Dumping module `%s'.\n", module->name.c_str()); dump_module(*f, "", module); } auto_name_map.clear(); reg_wires.clear(); } } VerilogBackend; PRIVATE_NAMESPACE_END