Memory elements of a RamNet

From Verific Design Automation FAQ
Jump to: navigation, search

In Verific statically-elaborated netlist, there are identifiers marked as possible RamNet.

In Verific netlist database, a RamNet is created for every identifier in the parsetree that is inferred as multi-port memory.

This example checks what identifiers in the elaborated parsetree can become RamNet, and checks for RamNets in the netlist database.

C++:

#include "veri_file.h"      // Make Verilog reader available
#include "VeriModule.h"     // Definition of a VeriModule and VeriPrimitive
#include "VeriId.h"         // Definitions of all identifier definition tree nodes
#include "VeriScope.h"      // Symbol table of locally declared identifiers
#include "DataBase.h"
#include <iostream>

#ifdef VERIFIC_NAMESPACE
using namespace Verific ;
#endif
using namespace std;

void checkMemory(VeriModule *mod)
{
   cout << "   in module: " << mod->Name() << "\n" ;
   VeriScope *scope = mod->GetScope() ;
   Set local_ids ;
   scope->GetDeclaredLocalIds(local_ids) ;
   SetIter si ;
   VeriIdDef *id ;
   FOREACH_SET_ITEM(&local_ids, si, &id) {
      if (!id) continue ;
      if (id->CanBeMultiPortRam()) {
         cout << "     " << id->Name() <<" can be RamNet\n";
         if (id->IsUsed()) {
            cout << "         can have read port\n";
         }
         if (id->IsAssigned()) {
            cout << "         can have write port\n";
         }
      }
   }
}

void checkMemory(Netlist* netlist)
{
    cout << "   in netlist: " << netlist->Owner()->Name() << "\n" ;
    // Traverse all the multiport memory nets of the netlist to check the portrefs.
    Net *net ;
    MapIter mi ;
    FOREACH_NET_OF_NETLIST(netlist, mi, net) {
        // Only interested in RamNet
        if (!net || !net->IsRamNet()) continue ;

        cout << "     " << net->Name() <<" is RamNet\n";
        unsigned hasReadPort = 0 ;
        unsigned hasWritePort = 0 ;
        unsigned hasClockedWritePort = 0;

        // Traverse all the portrefs of the net to check if the portref is connected to any ram ports.
        SetIter si ;
        PortRef *pr ;
        FOREACH_PORTREF_OF_NET(net,si, pr) {
            Instance *inst = pr ? pr->GetInst() : 0 ;
            if (!inst) continue ;

            if (inst->Type() == OPER_READ_PORT) {
                // This net is connected to a memory read port
                hasReadPort = 1 ;
            }
            if (inst->Type() == OPER_WRITE_PORT) {
                // This net is connected to a memory write port
                hasWritePort = 1;
            }
            if (inst->Type() == OPER_CLOCKED_WRITE_PORT) {
                // This net is connected to a memory clocked write port
                hasClockedWritePort = 1;
            }
        }
        if (hasReadPort) cout << "         has read port\n";
        if (hasWritePort) cout << "         has write port\n";
        if (hasClockedWritePort) cout << "         has clockedwrite port\n";
    }
}

// Netlist database - collect netlists in the design hiearchy
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) ;
}

// Parsetree - collects module in the design hiearchy
void Accumulate(VeriModule *module, Set &done)
{
    if (!module) return ; // Ignore NULL netlists
    SetItem *item = done.GetItem(module) ;
    if (item) {
        return ; // We've already been here
    }

    // Get the scope of the module:
    VeriScope *scope = module->GetScope() ;
    // Find all the declared ids in this scope:
    Map *ids = scope ? scope->DeclArea() : 0 ;
    MapIter mi ;
    VeriIdDef *id ;
    FOREACH_MAP_ITEM(ids, mi, 0, &id) { // Traverse declared ids
        if (!id || !id->IsInst()) continue ; // Consider only the instance ids
        VeriModuleInstantiation *mod_inst = id->GetModuleInstance() ; // Take the module instance
        VeriModule *mod = mod_inst ? mod_inst->GetInstantiatedModule() : 0 ; // The module instance is a module
        if (mod) { // This is verilog module instantiation: need to go into that module
            Accumulate(mod, done) ; // Traverse the instantiated module
        }
    }

    // Insert the traversed module
    done.Insert(module) ;
}

int main (int argc, char **argv)
{
    const char *file_name = "test.sv" ; // defaulf input filename
    if (argc > 1) file_name = argv[1] ; // Set the file name as specified by the user

    // Make sure memory inference is enabled
    RuntimeFlags::SetVar("veri_extract_multiport_rams", 1) ;
    // Runtime options related to memory inference - enable them as needed
    RuntimeFlags::SetVar("veri_allow_asynchronous_ram", 1) ;
    // RuntimeFlags::SetVar("veri_allow_ram_with_constant_index", 1) ;
    // RuntimeFlags::SetVar("veri_allow_any_ram_in_loop", 1) ;
    // RuntimeFlags::SetVar("veri_allow_ram_reset", 1) ;
    RuntimeFlags::SetVar("veri_minimum_ram_size", 16) ;

    if (!veri_file::Analyze(file_name, veri_file::SYSTEM_VERILOG)) return 1 ;
    if (!veri_file::ElaborateAllStatic()) return 1 ;

    Array *all_top_modules = veri_file::GetTopModules("work") ;
    VeriModule *top_module = (all_top_modules && all_top_modules->Size()) ? (VeriModule *)all_top_modules->GetFirst() : 0 ;

    cout << "* From elaborated parsetree *\n" ;

    Set modules(POINTER_HASH);
    Accumulate(top_module, modules);

    VeriModule *module ;
    SetIter si ;
    // FOREACH_SET_ITEM(&modules, si, &module) { // if you want to print top module last
    FOREACH_SET_ITEM_BACK(&modules, si, &module) { // if you want to print top module first
        if (module) {
            checkMemory(module);
        }
    }

    // if (!veri_file::ElaborateAll()) return 1;
    if (!veri_file::Elaborate(top_module->Name())) return 1;
    
    Netlist *top = Netlist::PresentDesign() ;

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

    cout << "* From netlist database *\n" ;
    Netlist *netlist ;
    // FOREACH_SET_ITEM (&netlists, si, &netlist) { // if you want to print top netlist last
    FOREACH_SET_ITEM_BACK (&netlists, si, &netlist) { // if you want to print top netlist first
        // Skip primitives and operators
        if (netlist->IsPrimitive() || netlist->IsOperator()) continue ;
        checkMemory(netlist) ;
    }

   return 0 ;
}
 

test.sv:

module top (input clk, input [6:0]addr, input [7:0]in, output reg [7:0]out, output reg [7:0]out1);

test inst(.clk(clk), .write(1'b0), .addr(addr), .in(in), .out(out), .out1(out1));

endmodule


module test (input clk, write, input [6:0]addr, input [7:0]in, output reg [7:0]out, output reg [7:0]out1) ;
    reg [7:0]RAM[127:0] ;
    reg [7:0]RAM1[127:0] ;
    reg [7:0]RAM2[127:0] ;

    always @(posedge clk) begin
        if (write) begin
            RAM[addr] = in ;
            RAM1[addr] = in ;
        end
        out = RAM[addr] ;
        out1 = RAM2[addr] ;
    end
endmodule
 

Run:

$ test-linux 
-- Analyzing Verilog file 'test.sv' (VERI-1482)
test.sv(1): INFO: compiling module 'top' (VERI-1018)
* From elaborated parsetree *
   in module: top
   in module: test
     RAM can be RamNet
         can have read port
         can have write port
     RAM1 can be RamNet
         can have write port
     RAM2 can be RamNet
         can have read port
test.sv(1): INFO: compiling module 'top' (VERI-1018)
test.sv(8): INFO: compiling module 'test' (VERI-1018)
test.sv(11): WARNING: net 'RAM2' does not have a driver (VDB-1002)
* From netlist database *
   in netlist: top
   in netlist: test
     RAM is RamNet
         has read port
         has clockedwrite port
     RAM1 is RamNet
         has clockedwrite port
     RAM2 is RamNet
         has read port
$