Memory elements of a RamNet
From Verific Design Automation FAQ
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 $