Andy Tomlin Posted August 20, 2023 Report Share Posted August 20, 2023 I am working on a tool to allow the specification of architectural definition ( its open source https://bitbucket.org/arch2code/arch2code/src/main/ ) I want to be able to switch between two different implementations of a block. The interfaces will be defined in a shared base class, and there will be two derived classes per block. one a systemC transaction model, and one a systemC verification wrapper containing verilated code. Many of the pieces are working but I wanted to be able to configure which implementations are used at run time so we dont need to recompile for every test. The shared base class will derive from blockBase defined as follows class blockBase { public: virtual ~blockBase() = default; }; there is a factory class as follows class instanceFactory { public: instanceFactory(); // allow implementation to register thier constructors static void registerBlock(std::string blockType, std::function< std::unique_ptr< blockBase >(const char * blockName, uint64_t baseAddress_) > blockFactoryFunction) { getMap().emplace(blockType, blockFactoryFunction); } // allow top level to create mappings from instance names to block types prior to enumeration of model static void registerInstance(std::string instance, std::string blockType) { getInstMap().emplace(instance, blockType ); } static std::unique_ptr< blockBase > createInstance(const char * blockName, uint64_t baseAddress_) { // figure out what type of block to create for this instance auto instIt = getInstMap().find(blockName); std::string blockType; if (instIt != getInstMap().end()) { blockType = instIt->second; } else { Q_ASSERT(false, str(boost::format("Attempted to create an an unregistered instance %s") % blockName) ); } auto it = getMap().find(blockType); if (it != getMap().end()) { return it->second(blockName, baseAddress_); } else { Q_ASSERT(false, str(boost::format("Attempted to create an instance %s of an unregistered block type %s") % blockName % blockType) ); } } private: static std::map<std::string, std::function<std::unique_ptr< blockBase>(const char * blockName, uint64_t baseAddress_)> >& getMap() { static std::map<std::string, std::function<std::unique_ptr< blockBase>(const char * blockName, uint64_t baseAddress_)>> map; return map; } static std::map<std::string, std::string>& getInstMap() { static std::map<std::string, std::string> map; return map; } }; In the implementation of the block the class is defined as (note the dual implementation shared definition stuff not implemented yet, I wanted to get the factory working first): SC_MODULE(producer), blockBase { private: logBlock log_; uint64_t baseAddress; public: // src ports // test_rdy_vld rdy_vld_out< data_st > test_rdy_vld; // test_rdy_vld_burst rdy_vld_burst_out< rdyVldBurstSt < test_st, data_st > > test_rdy_vld_burst; // test_rdy_vld_burst_no_tracker rdy_vld_burst_out< rdyVldBurstSt < test_no_tracker_st, data_st > > test_rdy_vld_burst_no_tracker; // test_req_ack req_ack_out< data_st, data_st > test_req_ack; // test_vld_ack vld_ack_out< data_st > test_vld_ack; // test_rdy_ack rdy_ack_out< data_st > test_rdy_ack; // dst ports // channels producer(sc_module_name blockName, uint64_t baseAddress_); // GENERATED_CODE_END struct registerBlock { registerBlock() { // lamda function to construct the block instanceFactory::registerBlock("producer_model", [](const char * blockName, uint64_t baseAddress_) -> std::unique_ptr<blockBase> { return static_cast<std::unique_ptr<blockBase>> (std::make_unique<producer>(blockName, baseAddress_));} ); } }; static registerBlock registerBlock_; ~producer() override = default; }; The top block above in the hierarchy contains the instance of producer std::unique_ptr<producer> uProducer; in the constructor for top uProducer needs initializing. I tried instanceFactory::createInstance("uProducer", baseAddress_ + 0x0) but it complains that I cannot cast a std::unique_ptr<blockBase> to std::unique_ptr<producer>. I have tried various things. I could avoid using the std::unique_ptr directly and instead just use a producer * uProducer; Chatgpt has not helped, any ideas Quote Link to comment Share on other sites More sharing options...
Andy Tomlin Posted August 20, 2023 Author Report Share Posted August 20, 2023 I think I might have cracked it. uProducer( static_cast<producer*>( instanceFactory::createInstance("uProducer", baseAddress_ + 0x0).release() ) ) Quote Link to comment Share on other sites More sharing options...
Eyck Posted August 21, 2023 Report Share Posted August 21, 2023 I'd rather use dynamic_cast<producer*> as it checks that the pointer is of correct type. If not it returns a nullptr sp you might have to check this before using it. And you should publicly inherit of blockBase. Did you have a look at UVM-SystemC (https://systemc.org/overview/systemc-verification/)? There all those mechanics are available and well testet. And its purpose is to structure verification environments and testbenches... Quote Link to comment Share on other sites More sharing options...
Andy Tomlin Posted August 21, 2023 Author Report Share Posted August 21, 2023 16 hours ago, Eyck said: I'd rather use dynamic_cast<producer*> as it checks that the pointer is of correct type. If not it returns a nullptr sp you might have to check this before using it. And you should publicly inherit of blockBase. Did you have a look at UVM-SystemC (https://systemc.org/overview/systemc-verification/)? There all those mechanics are available and well testet. And its purpose is to structure verification environments and testbenches... I switched the code to instead use shared_ptr instead of unique_ptr. So the member initialization list uses uProducer(std::dynamic_pointer_cast<producerBase>( instanceFactory::createInstance(name(), "uProducer", "producer", baseAddress_ + 0x0))) There is no need to check the pointer in our use case as the code will just instantly crash on dereferencing the nullptr - although this should never happen as its all generated code, and should have already asserted in the instanceFactory I already switched blockBase to public, thanks. I will take a look at UVM-SystemC. Our goal is to have an end to end tool - from architecture, to modeling, HW dev and verification. We are using our model code and the generated connections as the verification framework. We have SystemVerilog code compiling with verilator and integrated with the model. You can find more info https://arch2code.org/ Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.