欢迎光临散文网 会员登陆 & 注册

闲话解耦

2023-05-04 10:26 作者:LeonardTX  | 我要投稿

虽说解耦是软件工程中常常用到的概念这些年也时常挂在嘴边,但是第一次听说还应该是十几年前做code review的时候听feng总说起的,“不懂得解耦的工程师都只能算初级工程师”。遗憾的是这几年发现不管title是Xx还是Xx、Xx的很多兄弟姐妹们似乎在工作过程中都搞不清楚到底什么是耦合和解耦,也不知道该怎么做好的解耦,突出表现在不合理的模块划分、模块A的逻辑隐藏式的依赖模块B的时序、模块内超超超长到几十个状态的FSM、模块间冗余却没有扩展性的interface……趁着路上闲来无事,再次讲讲我从前学到和自己工作中理解到的解耦到底是什么,该怎么做。 所谓耦合,从字面上理解就是模块之间的依赖关系。这里说的模块只是一个概念,大可以到cpu、gpu、npu、moderm这种子系统,小可以到某个module内部两段不同的功能逻辑。从系统设计的角度看,既然是把这些功能放进一个产品中,那必然意味着它们之间存在着耦合,解耦的作用是降低模块之间的依赖关系,把关系密切的功能放在一个模块,把模块内部的细节尽量不暴露给其它模块,把模块之间的关联尽量清晰,让每个模块的设计更加的独立。本质上就是一句话,内部高聚合,接口清晰,之间低耦合。 一般而言,解耦的好处在于降低模块设计复杂度、提高模块设计的鲁棒性、增强模块的IP能力(可复用性和可移植性)。首先要做好的就是功能划分,需要正确的判断不同的功能是该进行聚合还是应该解耦,常用的判断原则不外乎如下几点: 1. 功能之间是否存在运算依赖 举个简单的例子,c=a+b,d=e*c,f=h^i,其中c=a+b和d=e*c应该聚合,f=h^i可以解耦出去 2. 功能之间是否存在数据依赖 在进行c=a+b的时候,对数据a、b的load和对数据c的store,它们之间应该是解耦的;但是对数据a/b的大小端转换,是可以和对数据a、b的load聚合的 3. 功能之间是否存在软件关联 需要软件在一次操作中处理的功能,可以聚合在一起,反之则可以考虑解耦 解耦后的功能模块,一般特点如下: 1. 模块内部功能定义清晰且逻辑简单 2. 模块对外接口明确且容易扩展 3. 模块之间无内在/隐含依赖关系 4. 模块之间的交互明确且最小化 5. 模块本身易于理解可读性强 凡事皆有例外,以上特点在特殊场景下不成立,譬如CPU核的流水线控制。 写到这里感觉依然很抽象,下面具体以十几年前做过的一个模块CipherHWA设计为例,介绍下如何进行良好的模块划分和解耦的

CipherHWA用于3G/4G的数据加解密和完整性保护,从功能上划分,它被切割成了不同的四个单元: 1. 进行加解密和完整性保护的运算单元SCRT 2. 完成PDCP PDU/SDU load/store的数据搬移单元DCH 3. 基于producer-consumer模型的软硬件接口self_config 4. 用于密钥派生的独立单元KDF 每个单元都完成自己的独立功能,单元之间的接口则尽量干净,某个单元对其它单元的功能无感。以SCRT和DCH为例,它们之间的接口仅仅是一个数据FIFO,SCRT对PDU/SDU在内存和总线上的搬移完全无感知,仅仅关注需要从FIFO中push/pop多少数据;DCH不关注数据是否进行加解密和采用了哪种加解密算法,仅仅关心需要load/store多少数据。SCRT/DCH均不关心PDU/SDU在内存中的存储格式、软件配置descriptor的格式、软件使用加速器的流程,self_config将descriptor解析后按照单个包为单位将它们所需要的信息发送到模块端口上并完成req/done握手。 完成顶层单元定义后,对下一次单元还可以继续进行解耦设计,譬如:SCRT可以进一步划分为用于加解密的UEA/EEA和用于完整性保护的UIA/EIA,按照算法可以再进一步划分为no operation、kasumi、snow-3g、aes、zuc;pdu/sdu descriptor可以被分割成通信协议部分和数据包部分,其中数据包直接提供给DCH使用,通信协议部分则由self_config调度…… 最后再提个问,3gpp协议中的golden c提供的测试数据,没记错的话是大端格式,通信系统中处理的数据,常常是小端格式。启动新项目或来了新BSP同事之后,常常在数据格式上折腾许久。那数据格式的转化,到底是该解耦成独立的单元,还是聚合到某个单元上更好,如果聚合的话该聚合在什么位置?没记错的话这问题当初我花了好几个星期才让Zx兄弟们搞明白该怎么解… 来个题外话,现在看到十几年前自己画的图,也实在是弱了点。看事情的逻辑、解决问题的方法、画图的能力、coding的水平,其实都是很重要的。

闲话解耦的评论 (共 条)

分享到微博请遵守国家法律