Buffering signals and ungrouping

From Verific Design Automation FAQ
Jump to: navigation, search

During ungrouping (flattening) a hierarchical design, there are nets that need to be merged. The name of the resulting net from the merge will be the name in the highest level of the design hierarchy.

This application example shows how to preserve the net name in the lower hierarchy level by adding a buffer to the net before ungrouping. In particular, it preserves the names of the output flip-flops in lower hierarchy levels.

#include "Map.h"
#include "Set.h"
#include "Message.h"
#include "veri_file.h"
#include "DataBase.h"
#include "Netlist.h"
#include "Array.h"
#include "VeriWrite.h"
#include "RuntimeFlags.h"

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif

static void
Flatten(Netlist *top_netlist)
{
    Set all_netlists(POINTER_HASH) ;
    top_netlist->Hierarchy(all_netlists,1/*top-to-bottom*/) ;

    // Iterate over all netlists in which we will do flattening :
    SetIter si ;
    Instance *inst ;
    Netlist *netlist ;
    FOREACH_SET_ITEM(&all_netlists, si, &netlist) {
        if (!netlist) continue ;
        // Collect all instances in this netlist :
        Array insts(netlist->NumOfInsts()) ;
        MapIter mi ;
        FOREACH_INSTANCE_OF_NETLIST(netlist, mi, inst) {
            insts.InsertLast(inst) ;
        }
        // Flatten the instances
        while (insts.Size()!=0) {
            inst = (Instance*)insts.RemoveLast() ;
            if (inst->IsPrimitive()) continue ;
            (void) inst->Flatten() ;
        }
        top_netlist->PropagateConstants() ;
    }
}

static void
BufferFlipflops(Netlist *netlist)
{
    if (!netlist) return ; // nothing to do.
    if (netlist->IsBlackBox()) return; // nothing to do.
    if (netlist->IsEmptyBox()) return; // nothing to do.

    MapIter mii ;
    Instance *inst ;
    FOREACH_INSTANCE_OF_NETLIST(netlist, mii, inst) {
        if (inst->Type()==PRIM_DFFRS) {
            // This is the instantiation of a flip-flop, buffer its output
            Net *onet = inst->GetOutput() ;
            if (onet) {
                Net *new_onet = netlist->Buf(onet, onet->Linefile()); // Add a buffer to the DFF output
                char *new_name = Strings::save("new_", onet->Name()); // Name the output of the buffer
                new_onet->SetName(new_name);
                Strings::free(new_name);
                SetIter si;
                Port *port;
                FOREACH_PORT_OF_NET (onet, si, port) { // move ports
                    if (!port) continue;
                    new_onet->Connect(port);
                    onet->Disconnect(port);
                }
            }
        }
    }
}

// This function is recursive in nature, and collects all other
// netlists that the incoming netlist depends on in a container.

void Accumulate(Netlist *netlist, Set &done)
{
    if (!netlist) return ; // Ignore NULL netlists

    SetItem *item = done.GetItem(netlist) ;
    if (item) {
        return ; // We've already been here
    }

    Instance *inst ;
    MapIter mi ;
    FOREACH_INSTANCE_OF_NETLIST(netlist, mi, inst) {
        // Now go into the netlist associated with the instance
        Accumulate(inst->View(), done) ;
    }

    // Insert the traversed Netlist
    done.Insert(netlist) ;
}

int main(int argc, char **argv)
{
    if (argc < 2) Message::PrintLine("reading default input file: test.v. Specify command line argument to override") ;

    const char *file_name = 0 ;
    if (argc>1) {
        file_name = argv[1] ; // Set the file name as specified by the user
    } else {
        file_name = "test.v" ; // Set default file name
    }
    // Now read in top-level design. In case of failure return.
    if (!veri_file::Read(file_name,"work", veri_file::VERILOG_2K)) {
        // Here, design analysis and elaboration failed
        return 1 ;
    }

    // Get a handle to the top-level netlist
    Netlist *top = Netlist::PresentDesign() ;
    if (!top) {
        Message::PrintLine("cannot find any handle to the top-level netlist") ;
        return 4 ;
    }

    // Print out module that we have handle to
    Message::Msg(VERIFIC_INFO, 0, top->Linefile(), "top level design is %s(%s)",
                 top->Owner()->Name(), top->Name()) ;

    // RuntimeFlags::SetVar("db_verilog_writer_write_structural_instance_names", 1);
    VeriWrite veriWriter;
    veriWriter.WriteFile ("nl_before.v", top);

    // Lets accumulate all netlist
    Set netlists(POINTER_HASH) ;
    Accumulate(top, netlists) ;

    // Traverse the design and add buffers to output of flipflops
    Netlist *netlist ;
    SetIter si ;
    FOREACH_SET_ITEM(&netlists, si, &netlist) {
        BufferFlipflops(netlist) ;
    }
    veriWriter.WriteFile ("nl_after.v", top);

    Flatten(top);
    veriWriter.WriteFile ("flat_after.v", top);

    // All done.  Wasn't that easy ?
    return 0 ;
}
 

RTL input:

module top (clk, reset, a, b, s);
  input clk, reset;
  input [1:0] a, b;
  output [1:0] s;
  wire [1:0] ta, tb, ts;
  bot ia (clk, reset, a, ta);
  bot ib (clk, reset, b, tb);
  assign ts = ta + tb;
  bot is (clk, reset, ts, s);
endmodule

module bot (clk, reset, input_d, output_q);
  input clk, reset;
  input [1:0] input_d;
  output [1:0] output_q;
  reg [1:0] output_q;
  always @ (posedge clk or posedge reset) begin
     if (reset)
         output_q <= 2'b00;
     else
         output_q <= input_d;
  end
endmodule
 

Flattened netlist without buffering:

module top (clk, reset, a, b, s);   // test.v(1)
    input clk;   // test.v(2)
    input reset;   // test.v(2)
    input [1:0]a;   // test.v(3)
    input [1:0]b;   // test.v(3)
    output [1:0]s;   // test.v(4)

    wire [1:0]ta;   // test.v(5)
    wire [1:0]tb;   // test.v(5)
    wire [1:0]ts;   // test.v(5)

    wire \add_3/cout , \add_3/n2 ;

    VERIFIC_DFFRS \is/i5  (.d(ts[1]), .clk(clk), .s(1'b0), .r(reset),
            .q(s[1]));   // test.v(21)
    VERIFIC_DFFRS \is/i6  (.d(ts[0]), .clk(clk), .s(1'b0), .r(reset),
            .q(s[0]));   // test.v(21)
    VERIFIC_FADD \add_3/i1  (.cin(1'b0), .a(ta[0]), .b(tb[0]), .o(ts[0]),
            .cout(\add_3/n2 ));   // test.v(8)
    VERIFIC_FADD \add_3/i2  (.cin(\add_3/n2 ), .a(ta[1]), .b(tb[1]), .o(ts[1]),
            .cout(\add_3/cout ));   // test.v(8)
    VERIFIC_DFFRS \ib/i6  (.d(b[0]), .clk(clk), .s(1'b0), .r(reset),
            .q(tb[0]));   // test.v(21)
    VERIFIC_DFFRS \ib/i5  (.d(b[1]), .clk(clk), .s(1'b0), .r(reset),
            .q(tb[1]));   // test.v(21)
    VERIFIC_DFFRS \ia/i6  (.d(a[0]), .clk(clk), .s(1'b0), .r(reset),
            .q(ta[0]));   // test.v(21)
    VERIFIC_DFFRS \ia/i5  (.d(a[1]), .clk(clk), .s(1'b0), .r(reset),
            .q(ta[1]));   // test.v(21)

endmodule
 

Final flattened netlist with buffering. Note the net names of the flipflop outputs.

module top (clk, reset, a, b, s) ;   // test.v(1)
    input clk;   // test.v(2)
    input reset;   // test.v(2)
    input [1:0]a;   // test.v(3)
    input [1:0]b;   // test.v(3)
    output [1:0]s;   // test.v(4)

    wire [1:0]\is/output_q ;   // test.v(15)
    wire [1:0]ta;   // test.v(5)
    wire [1:0]tb;   // test.v(5)
    wire [1:0]ts;   // test.v(5)
    wire [1:0]\ib/output_q ;   // test.v(15)
    wire [1:0]\ia/output_q ;   // test.v(15)

    wire \is/new_output_q[1] , \ib/new_output_q[1] , \add_3/cout , \add_3/n2 ,
        \is/new_output_q[0] , \ib/new_output_q[0] , \ia/new_output_q[1] ,
        \ia/new_output_q[0] ;

    buf (\is/new_output_q[0] , \is/output_q [0]) ;   // test.v(15)
    buf (\is/new_output_q[1] , \is/output_q [1]) ;   // test.v(15)
    VERIFIC_FADD \add_3/i1  (.cin(1'b0), .a(ta[0]), .b(tb[0]), .o(ts[0]),
            .cout(\add_3/n2 ));   // test.v(8)
    VERIFIC_DFFRS \is/i6  (.d(ts[0]), .clk(clk), .s(1'b0), .r(reset),
            .q(\is/output_q [0]));   // test.v(21)
    VERIFIC_DFFRS \is/i5  (.d(ts[1]), .clk(clk), .s(1'b0), .r(reset),
            .q(\is/output_q [1]));   // test.v(21)
    VERIFIC_FADD \add_3/i2  (.cin(\add_3/n2 ), .a(ta[1]), .b(tb[1]), .o(ts[1]),
            .cout(\add_3/cout ));   // test.v(8)
    buf (\ib/new_output_q[1] , \ib/output_q [1]) ;   // test.v(15)
    buf (\ib/new_output_q[0] , \ib/output_q [0]) ;   // test.v(15)
    VERIFIC_DFFRS \ib/i6  (.d(b[0]), .clk(clk), .s(1'b0), .r(reset),
            .q(\ib/output_q [0]));   // test.v(21)
    VERIFIC_DFFRS \ib/i5  (.d(b[1]), .clk(clk), .s(1'b0), .r(reset),
            .q(\ib/output_q [1]));   // test.v(21)
    buf (\ia/new_output_q[1] , \ia/output_q [1]) ;   // test.v(15)
    buf (\ia/new_output_q[0] , \ia/output_q [0]) ;   // test.v(15)
    VERIFIC_DFFRS \ia/i6  (.d(a[0]), .clk(clk), .s(1'b0), .r(reset),
            .q(\ia/output_q [0]));   // test.v(21)
    VERIFIC_DFFRS \ia/i5  (.d(a[1]), .clk(clk), .s(1'b0), .r(reset),
            .q(\ia/output_q [1]));   // test.v(21)

endmodule