tag 标签: 代码规范

相关博文
  • 热度 45
    2013-12-23 16:21
    4374 次阅读|
    18 个评论
         我要无辜的坦白说,您遇到标题党了。      没有血案,但有些冲突。 不是代码bug,我要讲注释风格。      这位看官,既然来了,且读且评吧。故事是真实的,如有雷同纯属巧合。      事情是这样的,有人离职,公司调我补缺。那个系统一直有个工程师在维护,参与该系统的新人来了又走,他始终泰若磐石。 刚过去一个礼拜,我就心下窃吼:“TMD坑爹啊!”,也彻底体会到什么叫---绝对的 权威、专家。 该软件系统,是公司发家的支柱产品,涉及十几个国家x七八个机型,700+个文件(C语言,所有.c .h档 ),十几万行代码,尽两百个跨模块全局变量,编译条件成百上千......(好吧,这是找借口、吐槽的节奏)      尽管有名义上的交接,和几次和声细语的密切指导。但真正独立展开工作时,许多小功能增删改,我都要折腾个好几天才能厘清代码调用关系、算法原理,然后找到修改点谨慎细微龟速前行。进度追踪的人有的时候也催毛了,直接请专家大师出手,咔咔咔,不到半天就搞定了。当然,前期我当然尽量的”不耻上问”,但人吗,总是有遇到被激发“骨气”的时候。      这几天,好不容易搞好一个全新的功能模块,把代码推送给他合并编译。不曾想,出现了让我泣血的一幕。      我眼睁睁地、眼睁睁看着她把我辛苦敲入的代码注释/**/通通换成//,一份精美的代码,顿时成渣。      我询问缘由,她的回答是: 当代码里面充满注释符号/**/时,她想要用/**/注释整段代码时,就会很麻烦。 我理解了,因为/**/不支持嵌套。比如下面这样的语句是编译不过的。          /*         a = b+c;  /* 注释 */ */ 复制代码      我的手抬了抬,终究作罢。虽然 我感觉到尊严被践踏,心爱的作品被蹂躏,但 我还是开始反思。      许多软件规范、专家、有经验的工程,都建议或要求注释代码最好使用 /**/,他们的理由大略如下: 1.  “//” 的注释的严密性不够。 例如          // 注释语句 ??/   a = b+c; 复制代码   此时,a = b+c在一些编译器不会被执行。   因为"??/"会被编译器当作 \,变成C语言的换行符。于是这段代码等同于             // 注释语句   a = b+c ; 复制代码    就会被注释掉。大家有兴趣的,不妨去搜索一下"C语言  三字母词 "   当然,哪怕没有 ??/ , 自己打盹碰到delete键也是会屏蔽掉a=b+c的   2. “//” 的注释,是C++发展后才引进的。 有些早期的C编译器对这种注释是不支持的。 代码要做到全平台兼容,这点是必需要考虑的。 因此,老外定义的C语言软件规范,无论是MISRA还是CMMI,一般都要求所有代码注释必须使用/**/。 君不见,那uCOS的最新版本源码,所有注释都是/**/。 君不见,那STM32的最新固件库,洋洋洒洒几十个文件,通篇皆没有用到//。   正是基于这样的理由,让我的心中充满了愠怒。但我仍然没有当场反驳她,因为这些理由还有些苍白无力。 1. 当时,那个什么三字母词“??x”到底是什么我已经忘了,没法立刻做试验编译给她看。 而且时候我里面作了编译实验,得到的是 "filename.c", line xxxx: Warning:  #2532-D: support for trigraphs is disabled xx代码语句xx //  ??/ trigraph金山词霸--- 三字母词 看吧,编译器都警告了,默认是不支持的。 2.  而且,所谓的//是C++的,早期的c编译器不支持。   这点谁鸟啊,我们只要现在,只用最新版本的编译器。   所以,我还要继续思考。 我要维护这个传统,为自己代言...... 下接: 一线研发之声 之 C代码注释引发的“血案” (二)  
  • 热度 7
    2013-7-15 23:37
    3676 次阅读|
    2 个评论
      1、设计中的FIFO、状态机接口需要有异常恢复状态和状态上报机制,格雷码电路防止被综合电路优化掉。 a)自行设计的格雷码FIFO(一般用于连续数据流跨时钟域)用Synplify综合时,为了防止被优化需要添加综合引导语句:“synthesis_syn_preserve = 1”; b)各种综合工具均有状态机安全模式,综合时候建议打开。   2、电路中所以寄存器、状态机、计数器、FIFO在单板上电复位时以及使用前必须处于一个已知状态。 a)对电路中的寄存器、状态机、计数器、FIFO必须进行异步复位(不依赖于任何时钟的复位); b)电路中的状态机、计数器在应用的时候不能完全依赖于异步复位时的状态,对于重要的状态机或计数器,必须还有周期检测或同步并对它进行复位/置数机制,保证可靠工作。   3、跨时钟域以及异步信号必须同步化处理(使用的QuartusII中的Design Assistant或者专业的nlint、spyglass等工具完成代码检查),这条是用FPGA进行数字电路设计的最核心最基本的思想和方法。 a)尽可能在整个设计中只用一个主时钟,同时只用一个时钟沿,主时钟走全局时钟网络; b)推荐所有输入输出信号均通过寄存器寄存,寄存器接口当作异步接口考虑; c)当全部电路不能用同步电路思想设计时,即需要用多个时钟来实现,则可以将全部电路分成若干个局部同步电路(尽可能以同一个时钟为一个模块),局部同步电路之间的接口当作异步接口考虑; d)电路的实际最高工作频率不应大于理论最高工作频率,要留有一定设计余量,保证芯片可靠工作; e)电路中所有寄存器、状态机在单板上电复位时应处在一个已知状态; f)对于设计中的异步电路,要给出不能转换为同步设计的原因,并对该部分异步电路的工作可靠性(如时钟等信号是否有毛刺,建立保持时间是否满足要求等)做出分析判断,并提供分析报告; e)关于全局时钟的约束,能上全局的全部上全局,不能上全局的建议通过区域时钟约束、逻辑锁定、增量编译等保证性能; h)依靠QuartusII中的Design Assistant或者专业的nlint/spyglass工具检查跨时钟域代码处理部分。   4、电路中不能出现门控时钟和行波时钟。 a)门控时钟的使用主要是通过关断时钟来达到降低功耗的目的,但是使用不当容易使得时钟出现毛刺,给设计带来灾难性风险。如果降功耗必要的话,推荐使用厂家自带的时钟控制的IP Core,例如ALTCLKCTRL; b)行波时钟是指寄存器输出的数据又作为下一个寄存器的时钟使用。行波时钟是一个非常危险的设计,由于寄存器有Tco,它会使时钟沿变缓,延时加大,多级级联的时候情况更加恶劣,出问题是必然的!行波时钟设计实际上是一种异步设计,大部分综合布线工具都不会对行波时钟设计进行setup/hold时序分析检查,设计无法保证正确性! 设计中完全没有必要使用行波时钟,可以用寄存器输出做同步使能用,与行波时钟设计意图完全一致。   5、需要综合的RTL源代码中不允许出现“*  /  %”这三个运算符。 a)目前大部分综合工具对以上三种运算支持的不好,而且非常浪费LE资源,还不能保证正确性和稳定性。可行的替代方案如下: “*”通过例化IP Core或者“移位运算+加法”来实现; “/”通过“移位运算+加法”来近似逼近实现,例如:1/30 = 1/32 + 1/512 + 1/8192; “%”通过自己手动计算得到; 以上运算在非综合的RTL的代码中使用不受此限制。   6、条件语句必须赋值完全,即:if语句后必须有配对else语句,case、casez、casex语句中必须有default语句。 a)如果if没有配对的else,case/casez/casex中没有对应的default语句的话,之后reg型数据会保持原来的值。这个在时序逻辑中可能没有太大的问题(如果设计者本意就是要保持的话),但在组合逻辑中会生成一个锁存器,锁存器逻辑往往不是设计者的意图而导致灾难性的后果。 为了避免不经意的犯错,建议养成良好的编码习惯:if语句一定要有配对的else,case/casez/casex中一定要有对应的default语句,即使要保持原来的值也要显示声明(else ;或者default ;)   7、使用器件的专用引脚和专用资源实现电路功能,这些包括全局时钟、复位管脚、全局输出允许管脚等,内部全局时钟网络等。 专用管脚和专用资源一定要优先使用,使用的好会事半功倍。如果不用器件提供的专用管脚或资源,有时导致稳定性和可靠性不高甚至是设计失败。   8、逻辑未使用的但又硬件连线的管脚,逻辑必须显示声明并处理。所有有硬件连线的管脚必须在顶层代码中显示声明(包括暂时没有使用的),不允许放任不管。 a)对于有硬件连线的输入信号(包括三态的输入方向),建议输入到CPU接口作为只读寄存器(如果没有CPU接口可以将该信号做成一些等效冗余逻辑加到其他逻辑中去),这样既可以消除Warning,又可以避免布线工具随便布线(把输出信号放到输入管脚)导致外部信号冲突; b)对于有硬件连线的输出信号(包括三态的输出方向),对于暂时没有使用的一定与硬件人员确定其默认值,避免对外信号(包括电阻上下拉)的干扰。   9、修改接口文档需要同对应项目组人员确认。 a)与硬件相关的方面涉及输入文件有qsf和sdc,内容包括但不限于电平标准、电流强度、管脚位置、输入输出方向、上下拉等等; b)与软件相关的方面涉及方案协同实现文档和寄存器接口文档,包括寄存器操作地址、操作顺序等等。  
  • 热度 21
    2013-7-15 22:03
    1428 次阅读|
    0 个评论
      1、设计中的FIFO、状态机接口需要有异常恢复状态和状态上报机制,格雷码电路防止被综合电路优化掉。 a)自行设计的格雷码FIFO(一般用于连续数据流跨时钟域)用Synplify综合时,为了防止被优化需要添加综合引导语句:“synthesis_syn_preserve = 1”; b)各种综合工具均有状态机安全模式,综合时候建议打开。   2、电路中所以寄存器、状态机、计数器、FIFO在单板上电复位时以及使用前必须处于一个已知状态。 a)对电路中的寄存器、状态机、计数器、FIFO必须进行异步复位(不依赖于任何时钟的复位); b)电路中的状态机、计数器在应用的时候不能完全依赖于异步复位时的状态,对于重要的状态机或计数器,必须还有周期检测或同步并对它进行复位/置数机制,保证可靠工作。   3、跨时钟域以及异步信号必须同步化处理(使用的QuartusII中的Design Assistant或者专业的nlint、spyglass等工具完成代码检查),这条是用FPGA进行数字电路设计的最核心最基本的思想和方法。 a)尽可能在整个设计中只用一个主时钟,同时只用一个时钟沿,主时钟走全局时钟网络; b)推荐所有输入输出信号均通过寄存器寄存,寄存器接口当作异步接口考虑; c)当全部电路不能用同步电路思想设计时,即需要用多个时钟来实现,则可以将全部电路分成若干个局部同步电路(尽可能以同一个时钟为一个模块),局部同步电路之间的接口当作异步接口考虑; d)电路的实际最高工作频率不应大于理论最高工作频率,要留有一定设计余量,保证芯片可靠工作; e)电路中所有寄存器、状态机在单板上电复位时应处在一个已知状态; f)对于设计中的异步电路,要给出不能转换为同步设计的原因,并对该部分异步电路的工作可靠性(如时钟等信号是否有毛刺,建立保持时间是否满足要求等)做出分析判断,并提供分析报告; e)关于全局时钟的约束,能上全局的全部上全局,不能上全局的建议通过区域时钟约束、逻辑锁定、增量编译等保证性能; h)依靠QuartusII中的Design Assistant或者专业的nlint/spyglass工具检查跨时钟域代码处理部分。   4、电路中不能出现门控时钟和行波时钟。 a)门控时钟的使用主要是通过关断时钟来达到降低功耗的目的,但是使用不当容易使得时钟出现毛刺,给设计带来灾难性风险。如果降功耗必要的话,推荐使用厂家自带的时钟控制的IP Core,例如ALTCLKCTRL; b)行波时钟是指寄存器输出的数据又作为下一个寄存器的时钟使用。行波时钟是一个非常危险的设计,由于寄存器有Tco,它会使时钟沿变缓,延时加大,多级级联的时候情况更加恶劣,出问题是必然的!行波时钟设计实际上是一种异步设计,大部分综合布线工具都不会对行波时钟设计进行setup/hold时序分析检查,设计无法保证正确性! 设计中完全没有必要使用行波时钟,可以用寄存器输出做同步使能用,与行波时钟设计意图完全一致。   5、需要综合的RTL源代码中不允许出现“*  /  %”这三个运算符。 a)目前大部分综合工具对以上三种运算支持的不好,而且非常浪费LE资源,还不能保证正确性和稳定性。可行的替代方案如下: “*”通过例化IP Core或者“移位运算+加法”来实现; “/”通过“移位运算+加法”来近似逼近实现,例如:1/30 = 1/32 + 1/512 + 1/8192; “%”通过自己手动计算得到; 以上运算在非综合的RTL的代码中使用不受此限制。   6、条件语句必须赋值完全,即:if语句后必须有配对else语句,case、casez、casex语句中必须有default语句。 a)如果if没有配对的else,case/casez/casex中没有对应的default语句的话,之后reg型数据会保持原来的值。这个在时序逻辑中可能没有太大的问题(如果设计者本意就是要保持的话),但在组合逻辑中会生成一个锁存器,锁存器逻辑往往不是设计者的意图而导致灾难性的后果。 为了避免不经意的犯错,建议养成良好的编码习惯:if语句一定要有配对的else,case/casez/casex中一定要有对应的default语句,即使要保持原来的值也要显示声明(else ;或者default ;)   7、使用器件的专用引脚和专用资源实现电路功能,这些包括全局时钟、复位管脚、全局输出允许管脚等,内部全局时钟网络等。 专用管脚和专用资源一定要优先使用,使用的好会事半功倍。如果不用器件提供的专用管脚或资源,有时导致稳定性和可靠性不高甚至是设计失败。   8、逻辑未使用的但又硬件连线的管脚,逻辑必须显示声明并处理。所有有硬件连线的管脚必须在顶层代码中显示声明(包括暂时没有使用的),不允许放任不管。 a)对于有硬件连线的输入信号(包括三态的输入方向),建议输入到CPU接口作为只读寄存器(如果没有CPU接口可以将该信号做成一些等效冗余逻辑加到其他逻辑中去),这样既可以消除Warning,又可以避免布线工具随便布线(把输出信号放到输入管脚)导致外部信号冲突; b)对于有硬件连线的输出信号(包括三态的输出方向),对于暂时没有使用的一定与硬件人员确定其默认值,避免对外信号(包括电阻上下拉)的干扰。   9、修改接口文档需要同对应项目组人员确认。 a)与硬件相关的方面涉及输入文件有qsf和sdc,内容包括但不限于电平标准、电流强度、管脚位置、输入输出方向、上下拉等等; b)与软件相关的方面涉及方案协同实现文档和寄存器接口文档,包括寄存器操作地址、操作顺序等等。  
  • 热度 24
    2013-7-15 21:58
    1622 次阅读|
    0 个评论
      1、设计中的FIFO、状态机接口需要有异常恢复状态和状态上报机制,格雷码电路防止被综合电路优化掉。 a)自行设计的格雷码FIFO(一般用于连续数据流跨时钟域)用Synplify综合时,为了防止被优化需要添加综合引导语句:“synthesis_syn_preserve = 1”; b)各种综合工具均有状态机安全模式,综合时候建议打开。   2、电路中所以寄存器、状态机、计数器、FIFO在单板上电复位时以及使用前必须处于一个已知状态。 a)对电路中的寄存器、状态机、计数器、FIFO必须进行异步复位(不依赖于任何时钟的复位); b)电路中的状态机、计数器在应用的时候不能完全依赖于异步复位时的状态,对于重要的状态机或计数器,必须还有周期检测或同步并对它进行复位/置数机制,保证可靠工作。   3、跨时钟域以及异步信号必须同步化处理(使用的QuartusII中的Design Assistant或者专业的nlint、spyglass等工具完成代码检查),这条是用FPGA进行数字电路设计的最核心最基本的思想和方法。 a)尽可能在整个设计中只用一个主时钟,同时只用一个时钟沿,主时钟走全局时钟网络; b)推荐所有输入输出信号均通过寄存器寄存,寄存器接口当作异步接口考虑; c)当全部电路不能用同步电路思想设计时,即需要用多个时钟来实现,则可以将全部电路分成若干个局部同步电路(尽可能以同一个时钟为一个模块),局部同步电路之间的接口当作异步接口考虑; d)电路的实际最高工作频率不应大于理论最高工作频率,要留有一定设计余量,保证芯片可靠工作; e)电路中所有寄存器、状态机在单板上电复位时应处在一个已知状态; f)对于设计中的异步电路,要给出不能转换为同步设计的原因,并对该部分异步电路的工作可靠性(如时钟等信号是否有毛刺,建立保持时间是否满足要求等)做出分析判断,并提供分析报告; e)关于全局时钟的约束,能上全局的全部上全局,不能上全局的建议通过区域时钟约束、逻辑锁定、增量编译等保证性能; h)依靠QuartusII中的Design Assistant或者专业的nlint/spyglass工具检查跨时钟域代码处理部分。   4、电路中不能出现门控时钟和行波时钟。 a)门控时钟的使用主要是通过关断时钟来达到降低功耗的目的,但是使用不当容易使得时钟出现毛刺,给设计带来灾难性风险。如果降功耗必要的话,推荐使用厂家自带的时钟控制的IP Core,例如ALTCLKCTRL; b)行波时钟是指寄存器输出的数据又作为下一个寄存器的时钟使用。行波时钟是一个非常危险的设计,由于寄存器有Tco,它会使时钟沿变缓,延时加大,多级级联的时候情况更加恶劣,出问题是必然的!行波时钟设计实际上是一种异步设计,大部分综合布线工具都不会对行波时钟设计进行setup/hold时序分析检查,设计无法保证正确性! 设计中完全没有必要使用行波时钟,可以用寄存器输出做同步使能用,与行波时钟设计意图完全一致。   5、需要综合的RTL源代码中不允许出现“*  /  %”这三个运算符。 a)目前大部分综合工具对以上三种运算支持的不好,而且非常浪费LE资源,还不能保证正确性和稳定性。可行的替代方案如下: “*”通过例化IP Core或者“移位运算+加法”来实现; “/”通过“移位运算+加法”来近似逼近实现,例如:1/30 = 1/32 + 1/512 + 1/8192; “%”通过自己手动计算得到; 以上运算在非综合的RTL的代码中使用不受此限制。   6、条件语句必须赋值完全,即:if语句后必须有配对else语句,case、casez、casex语句中必须有default语句。 a)如果if没有配对的else,case/casez/casex中没有对应的default语句的话,之后reg型数据会保持原来的值。这个在时序逻辑中可能没有太大的问题(如果设计者本意就是要保持的话),但在组合逻辑中会生成一个锁存器,锁存器逻辑往往不是设计者的意图而导致灾难性的后果。 为了避免不经意的犯错,建议养成良好的编码习惯:if语句一定要有配对的else,case/casez/casex中一定要有对应的default语句,即使要保持原来的值也要显示声明(else ;或者default ;)   7、使用器件的专用引脚和专用资源实现电路功能,这些包括全局时钟、复位管脚、全局输出允许管脚等,内部全局时钟网络等。 专用管脚和专用资源一定要优先使用,使用的好会事半功倍。如果不用器件提供的专用管脚或资源,有时导致稳定性和可靠性不高甚至是设计失败。   8、逻辑未使用的但又硬件连线的管脚,逻辑必须显示声明并处理。所有有硬件连线的管脚必须在顶层代码中显示声明(包括暂时没有使用的),不允许放任不管。 a)对于有硬件连线的输入信号(包括三态的输入方向),建议输入到CPU接口作为只读寄存器(如果没有CPU接口可以将该信号做成一些等效冗余逻辑加到其他逻辑中去),这样既可以消除Warning,又可以避免布线工具随便布线(把输出信号放到输入管脚)导致外部信号冲突; b)对于有硬件连线的输出信号(包括三态的输出方向),对于暂时没有使用的一定与硬件人员确定其默认值,避免对外信号(包括电阻上下拉)的干扰。   9、修改接口文档需要同对应项目组人员确认。 a)与硬件相关的方面涉及输入文件有qsf和sdc,内容包括但不限于电平标准、电流强度、管脚位置、输入输出方向、上下拉等等; b)与软件相关的方面涉及方案协同实现文档和寄存器接口文档,包括寄存器操作地址、操作顺序等等。  
相关资源