前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >UVM方法学与设计模式_4:策略模式 & UVM run_test

UVM方法学与设计模式_4:策略模式 & UVM run_test

作者头像
AsicWonder
发布2020-06-12 16:24:13
发布2020-06-12 16:24:13
66000
代码可运行
举报
运行总次数:0
代码可运行

Part 1. 策略模式代码示例

在我们的代码中,最常使用的控制语句恐怕非if...else...莫属。对if/else我们是又爱又恨,特别是当代码中出现大量的if/else/else if的时候,对于代码的维护者简直就是噩梦。

让我们直接来看一个示例:

代码语言:javascript
代码运行次数:0
运行
复制
class Invoker
{
public:
    Run(std::string moduleName, std::string state, bool bIsAll, bool bIsFull ... )
    {
        if (moduleName == "module0")
        {
            if (state == "state0")
            {
                module0State0();
            }
            if (state == "state1")
            {
                module0State0();
            }
            if (state == "state2")
            {
                module0State0();
            }
            if (state == "state3")
            {
                module0State0();
            }
        }
        else if (moduleName == "module1")
        {
            if (state == "state0")
            {
                module1State0();
            }
            if (state == "state1")
            {
                module1State0();
            }
            if (state == "state2")
            {
                module1State0();
            }
            if (state == "state3")
            {
                module1State0();
            }
        }
        else if (moduleName == "module2")
        {
            if (state == "state0")
            {
                module2State0();
            }
            if (state == "state1")
            {
                module2State0();
            }
            if (state == "state2")
            {
                module2State0();
            }
            if (state == "state3")
            {
                module2State0();
            }
        }
        else if (moduleName == "module3")
        {
            if (state == "state0")
            {
                module3State0();
            }
            if (state == "state1")
            {
                module3State0();
            }
            if (state == "state2")
            {
                module3State0();
            }
            if (state == "state3")
            {
                module3State0();
            }
        }
        ...
        else if (moduleName == "moduleN")
        {
            if (state == "state0")
            {
                moduleNState0();
            }
            if (state == "state1")
            {
                moduleNState0();
            }
            if (state == "state2")
            {
                moduleNState0();
            }
            if (state == "state3")
            {
                moduleNState0();
            }
        }
        else if (bIsAll && state == "state0")
        {
            module0State0();
            module1State0();
            module2State0();
            module3State0();
            ...
            moduleNState0();
        }
        else if (bIsAll && state == "state1")
        {
            module0State1();
            module1State1();
            module2State1();
            module3State1();
            ...
            moduleNState1();
        }
        else if (bIsAll && state == "state2")
        {
            module0State2();
            module1State2();
            module2State2();
            module3State2();
            ...
            moduleNState2();
        }
        else if (bIsAll && state == "state3")
        {
            module0State3();
            module1State3();
            module2State3();
            module3State3();
            ...
            moduleNState3();
        }
        else if (bIsFull && moduleName == "module0")
        {
            module0State0();
            module0State1();
            module0State2();
            module0State3();
        }
        else if (bIsFull && moduleName == "module1")
        {
            module1State0();
            module1State1();
            module1State2();
            module1State3();
        }
        else if (bIsFull && moduleName == "module2")
        {
            module2State0();
            module2State1();
            module2State2();
            module2State3();
        }
        else if (bIsFull && moduleName == "module3")
        {
            module3State0();
            module3State1();
            module3State2();
            module3State3();
        }
        ...
        else if (bIsFull && moduleName == "moduleN")
        {
            moduleNState0();
            moduleNState1();
            moduleNState2();
            moduleNState3();
        }
        ...
    }
};

示例代码中的 ... 代码此处省略“1万字”。(事实上,这是某生产环节中的实际代码的美化版本)

我们先简单梳理下这段代码在做什么。假如我们需要处理N个模块,分别为module0~moduleN,每个模块都有State0~State3四种状态,可以认为是四种power state。

这段代码所描述的是这样3件事:

1.可以单独对某个module设置为某种状态;

2.bIsAll为true时可以将所有module设置为某一状态;

3.bIsFull为true时可以将某个module依序设置为State0,1,2,3。

咋看之下,这段代码倒也不错,只不过是代码长度比较感人。然而事实上是,这里的示例经过了美化。

实际的代码中,每个if或者else if下未必是一个个包装好的函数,也许就是一段段裸露的底层代码

module名字也必然不会是module0,1,2,...如此规则;

状态名字也不会是state0,1,2,...如此规则。

如此冗长、复杂的代码却常常出现在我们的生产代码中,因为if/else/else if是“最简单”不过的添加功能的方式了。

更糟糕的是,如果有一天需要增加/删除一些module,增加/删除一些state,更改处理函数的内部逻辑,就不得不对这一长段代码进行从头到尾的修改。在复杂逻辑的掩盖下,修改的过程更容易引入新的bug,导致更多的修改工作量,更多的regression和debug过程。

然而事实上,你的修改跟大部分原来的逻辑功能并不冲突,原本你唯一需要debug的是你新增加的功能,但是这样看似简单的代码结构,其实严重违背了开闭原则,导致了潜在的更多的工作量。

策略模式很好的解决了这个问题。让我们试着发现这个模式。

一连串的if/else/else if其实所隐含的意义是:我有很多种解决方案,但是每次我会根据你的要求给出一种/某几种解决方案。

原来的代码之所以有这么大的缺陷,其根本问题是:它知道的太多了。其实我们所需要的代码无非是根据不同的输入给出不同的解决方案,至于解决方案具体长什么样并不用关心。如果我们能够抽象出所有解决方案,每次根据不同的输入要求,给出不同的解决方案(或者算法),那么就可以隔开方案具体实现选取分派过程,达到解耦的目的。

一种抽象,可替换的具体实现,这不就是我们之前一直在使用的技巧,多态,吗。

具体的我们只需要将不同的解决方案从原来的一段段裸露的代码块,包装成一个个具有同一基类的类中的具体处理方法。那么通过基类指针/引用,即可以调用每一种具体的/不同的解决方案/算法。

一种具体的实现方案如下:

代码语言:javascript
代码运行次数:0
运行
复制
class Module
{
public:
    Module(std::string moduleName) : m_moduleName(moduleName) {}
    virtual void ConfigIntoState(std::string state) = 0;

    void AddState(std::string state) {...}
    void RemoveState(std::string state) {...}
    std::vector<std::string>& GetStates() {...}

    std::string GetName()
{
        return m_moduleName;
    }

protected:
    std::string m_moduleName;
    std::vector<std::string> m_states;
};

class Module0 : public Module
{
public:
    Module0(std::string moduleName) : Module(moduleName) {}

    virtual void ConfigIntoState(std::string state)
{
        if (state == "state0")
            ...
        else if (state == "state1")
            ...
        else if (state == "state2")
            ...
        else if (state == "state3")
            ...
    }
};

class Module1 : public Module
{
public:
    Module1(std::string moduleName) : Module(moduleName) {}

    virtual void ConfigIntoState(std::string state)
{
        if (state == "state0")
            ...
        else if (state == "state1")
            ...
        else if (state == "state2")
            ...
        else if (state == "state3")
            ...
    }
};

class Module2 : public Module
{
public:
    Module2(std::string moduleName) : Module(moduleName) {}

    virtual void ConfigIntoState(std::string state)
{
        if (state == "state0")
            ...
        else if (state == "state1")
            ...
        else if (state == "state2")
            ...
        else if (state == "state3")
            ...
    }
};

class Invoker
{
public:
    void AddModule(Module *module)
{
        m_ModuleMap[module.GetName()] = module;
    }

    void Run(std::string moduleName, std::string state, bool bIsAll, bool bIsFull)
{
        if (bIsAll)
        {
            for (auto &kv : m_ModuleMap)
            {
                kv.second->ConfigIntoState(state);
            }
        }
        else if (bIsFull)
        {
            Module *module = m_ModuleMap[moduleName];
            for (auto &state : module->GetStates())
            {
                module->ConfigIntoState(state);
            }
        }
        else
        {
            m_ModuleMap[moduleName]->ConfigIntoState(state);
        }

    }

private:
    std::unordered_map<std::string, Module*> m_ModuleMap;
};
代码语言:javascript
代码运行次数:0
运行
复制

这种解决方法的好处就在于Run方法不再需要因为增加/删除/修改模块或状态而变动。

整个Run方法在一屏内就能显示完毕,其意图也一目了然。与此同时,增加和删除模块并不会对其他模块造成任何影响:因为他们完全在不同的类中。

同样的,对于state的处理,如果有需要对if/else/else if完全可以再进行一次类似的重构。这里不再赘述。

Part 2. UVM run_test

UVM 起不同test的run_test机制也采用了策略模式的思想。

当我们调用run_test时其实调用的是一个全局task,该task会去调用top的run_test成员方法。

其中最重要的是这个cast过程:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
复制
$cast(uvm_test_top, factory.create_component_by_name(test_name,"","uvm_test_top",null));
代码语言:javascript
代码运行次数:0
运行
复制
通过factory创建了一个具体的解决方案,并将其自动挂接到top下:

uvm_test_top自动挂接到uvm_root下

其自动挂接的过程是:

create_component的构造函数new中会判断该component的parent是否为null,如果为null则直接设置其parent为top,即uvm_root。而在前述创建具体解决方案的cast过程中,已经设置其parent为null了。

通过每次根据输入参数名,create不同的component作为uvm_test_top,并自动挂接到uvm tree下,从而实现了具体test(解决方案/算法)uvm框架运行机制的充分解耦。

Part 3. 总结

策略模式定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

授权转载于 知乎专栏《UVM方法学与设计模式》

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字芯片实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档