【Chips】VLSI Final Project:小型卷积核单元设计 项目总结

vivado 卷积编码ip核学习笔记和matlab仿真对照 coe置入rom ,rom ip核读数据会有一个周期的延迟?也就是说深度设置为100的话addr为1到100时才是正常所存储的数据。不管复位信号多早拉高 这一段固有,启动时间?那这一段握手成功的话会有一个数据的传输吗?卷积核ip核复位后 ready信号会拉高一个周期再拉低一个周期再拉高一个周期?注意matlab里仿真需要转至8进制且code1在前 分别为155 117。采用1/2编码效率 (2,1,7) 生成多项式ip核默认设置。设置里面有个输出寄存器,勾选的话会有两个周期的延迟。top程序和tb如下。 阅读详情

VLSI Final Project:小型卷积核单元设计 项目总结

0 项目背景

​ VLSI流程课程在期末要求完成一个project,来让我这样的小白简单走一遍VLSI的设计流程有个概念。作业内容是实现一个简单的小型卷积核,给定输入的数据后能计算并返回相应正确的卷积运算结果。整个project主要分功能仿真,然后是综合,再综合后仿真,形式验证。我个人任务主要是根据需要设计并完成verilog部分工作,队友完成了后续的综合、后仿真、形式验证等内容,所以我对verilog内容熟悉一些,也主要记录这一块的内容与感受。

​ 作为一个芯片、verilog等知识点的小白,边学边写,并在deadline截至前两天快速迭代代码版本着实是非常刺激。趁自己还记着点project细节,以及纪念一波撰写第一个小型verilog任务、实现了本project的前仿、综合、后仿、形式验证等环节,我记录一下此project的思路以及相关的基础知识。

1 项目要求

​ 设计简单的卷积核运算模块,限定了模块的输入端口、输出端口,在保证正确性的前提下能完整的通过后续的dc过程、后仿与形式验证等过程。在同等情况下, 卷积核单元的面积、速度、功耗将成为评判性能优劣的标准。(我并没有考虑这玩意儿( ̄▽ ̄)")

2 常规概念

​ 作为一个IC设计小白,有一些基础知识与概念得去了解以后才能实施后续的模块设计。简单把相关内容分为verilog与IC流程相关吧。

2.1 Verilog语法

​ 用程序的形式 “写出” 一个芯片 来, 那肯定是要用一个 “能够描写电路工作逻辑”的程序语言 来写才行吧!HDL,(Hardware Description Language),就是专门写电路运行逻辑的编程语言,比C语言更底层一丢丢,是所有能干这个事情的程序语言的大类名称。其中有两个很有名的常用语言,一个是VHDL,但是以前美国军方用的,咱不提他;另一个是Verilog语言,好,俺用的也是这个。所以Verilog语言属于HDL。

​ 我们用Verilog劈里啪啦写了一个project以后,这些代码文件后缀是.v格式的。

​ verilog的语法内容和C语言有很多是相像的,在学习的时候对照一下有那些相同点和不同点即可快速入门。在课程上有老师多次提到,在网络上也有别的博主说过:撰写verilog的时候,心中要有电路!起初我觉得这是一句废话,我写c语言的时候脑子也会有算法的运行逻辑,这不是很正常的吗?随着这个project的撰写推进,直到后续的dc环节、后仿真环节时,我才意识到这句话内部的真正含义:要想你你写的verilog模块得是真实、有效、能流片的芯片逻辑,必须要心中有“电路”而不是仅仅有逻辑而已!!!——verilog代码能运行,和能产生相对应的电路(即可综合)是两码事!( ̄▽ ̄)"

  • 相同点:
    • 注释、标识符命名规范、运算符(逻辑与或非、转义符等,但是verilog没有自增运算符,即‘i++’)
    • 常用的基本语句块,如if-else、for语句、while. 但是{}变成了begin和end.
  • 不同点:(仅写了部分,)
    • 所有.v文件写的时候要先声明模块名字module xxx(x..x); endmodule,这并不等价于c语言里的函数的概念,而是和matlab的所有.m文件的模块化一个意思。也可以理解为一个“类”,后续可以用这个module的名字进行实例化不同个数的模块。
    • 对于模块的参数,要在后文中写明是输入类型(input)还是输出类型(output),并声明其类别(是reg类还是wire等,不写就默认是wire类型).
    • 常量的定义语法不同,verilog中写法是parameter xx=...
    • verilog不怎么使用for语句,尽量少用,因为这样会让后续生成的电路比较复杂,一般都是无脑写出来(没错,是你想那样)。
    • verilog的switch和if-else语句,一定要把每个分支都考虑到位,switch要养成写default的习惯,不然后续综合出的电路多出来一堆latch的时候,有你好受的( ̄▽ ̄)".
    • verilog里最大的两个多出来的部分,一个是alway语句块,一个是assign赋值语句,这个学习一下即可。
    • verilog中多出来两个重要的变量类型,分别是reg和wire类型。要搞清楚它们之间的联系与区别,不然最基础的代码逻辑都没法写,这是俩verilog中最最常用也是最最核心的两个变量类型。一般来说,对于一个module的输入输出端口可以都是wire类型。但是在实例化模块的时候要区别对待:把形参是wire类型的输入端口变量用实参是reg类型的变量代进去。
    • veriog的过程赋值语句中分成了两种,一种是和c语言一样的自上而下顺序执行的语句:阻塞赋值(=);另一种是语句块里的内容同时执行的语句:非阻塞赋值(<=).

​ 在完成本项目的过程中,我起初并没有care这两种赋值手段的区别,完全按照c语言的编码思路。我当时捉摸着只要数据变量之间不存在dependency的话,在语句块中使用阻塞赋值和非阻塞赋值没啥区别。但是后来发觉,verilog在撰写模块的时候不用非阻塞赋值没法综合,也就是后续的环节走不下去,写的代码虽然功能上正确也能仿真出正确的结果和波形,但是无法生成相应的现实对应的电路图,那肯定不行啊( ̄▽ ̄)"。(很好理解,电路中的变化都是随着时钟的每一个上升沿或下降沿的cycle进行变化的,而各个部分都是同时发生变化而不是顺序串行变化的,所以在写时序逻辑代码的时候要用非阻塞赋值才能对应电路而不能用阻塞赋值)。所以在小白写verilog语句的时候,我觉得最重要的两个知识点,就是完全搞明白阻塞赋值versus非阻塞赋值,以及always模块的一些关键特性与执行逻辑(多个always是并行之类的)。

​ 这一部分呢,可以看这份博客,对于阻塞赋值和非阻塞赋值有一些更好的理解:

​ Verilog十大基本功0(阻塞赋值与非阻塞赋值)—— 优快云 Times_poem

​ Verilog HDL 阻塞和非阻塞赋值的理解(2) —— 优快云 ShareWow丶

2.2 Testbench的概念

​ 以前没写过verilog,接触早先的quartus9.1时也没有接触过testbench,所以对这个概念有点迷茫。实际上,在撰写verilog代码的时候,另一个有一定工作量的活儿就是写testbench。

  • 如何理解testbench?
    • 在最简单的C程序中,我们完全可以让我们需要运算、处理的数据内容、最初的数据信息在main()函数中、或某个自定义函数中进行初始化、引入。
    • 但verilog中并不行。用verilog写出来的程序模块只能进行“对数据的加工和处理”而不能进行初始化工作,因为整个模块(对应到现实中,就是这个模块对应的实例化出的电路)只要通电了以后就会不断的运行与工作,哪有什么“初始化”的概念。唯一一个相似的概念是——“复位”。但这并完全等价于初始化。
    • 可能读者会对什么情况下,数据需要进行初始化没有概念?举个例子,就是时钟信号 c l k clk clk, 这个一位的信号会隔固定的时间从0翻转到1,又过一会儿翻转回0. 因此,需要在某个地方声明时钟信号的翻转频率,这就是最简单的初始化语句(虽然会永远执行,但第一次执行时间是模块最开始的时候进行的)。
    • 在verilog中,其实存在一个用于初始化的语法——语句块:initial begin ... end,这句话的内容只会在模块被创建出后执行一次。但是!在综合环节中,verilog的模块时不能使用这句话的( ̄▽ ̄)",用了IDE也不会报错(会不会来着,忘记了),因为语法上没有问题,但是后续dc环节就无法综合出电路了…
    • 所以,为了进行输入数据的传递、初始化信号的传入,我们就需要写在一个专门的文件中——就是testbench。而testbench也是一个.v文件,只不过它不会参与后续芯片的后仿、综合等过程,仅仅是起到传输输入信号、数据的文件,所以是测试代码正确性、RTL波形仿真中不可缺少的伴随文件。而在testbench中啥都可以写,只要逻辑和语法对就行,所以可以使用intial语句块、以及时间延迟的 ‘#’ 符号等对数据进行处理。

2.3 基本的数字逻辑电路的知识

​ 本科要有学过的底子,有个大概就行,不用很好。

​ 学期初我一直不理解TTL和CMOS的区别。本科的时候只讲了TTL没有讲MOS,而读研讲MOS不讲TTL,但是它们都有类似的截止区、放大区、饱和区,工作特性类似,确实不同的结构。那它们有什么区别呢?为什么都是用CMOS进行集成电路设计呢?在网上搜了半天,没有得到想要的答案,结果翻书的时候,书上提到了并给出了总结:

  • TTL和CMOS集成电路的制造工艺不同,但是逻辑功能和应用上没有大区别,多相同。
  • ELC最快、TTL中等、CMOS最慢;
  • ELC抗干扰能力最弱、TTL中等、CMOS最好;
  • 功耗ELC最高、TTL中等、CMOS最低。

​ 而我们上课常提到的MOS反相器,又称为非门。在MOS集成电路中,各逻辑门都是由反相器(非门)构成的。所以它的重要性如此之高。

​ 还有一个要注意:在搞VLSI的时候,常常会看到有老师、博客会说 ”不要生成latch,要用FF代替latch“ 的话,那么什么是 ”latch“ 什么是 ”FF“ 呢,这个要注意一下。可以在网上搜一下,这个还是蛮多的。

2.4 VLSI的基础知识补充

​ 不像C语言中点击“编译并运行”就能看出结果是否够正确,因为我们verilog写代码的目的是写电路、写芯片,所以代码最后能跑出正确的结果也并不一定说明代码能用——因为可能产生不了对应电路、性能太辣鸡等等,所以RTL级别的验证只是最最基础的而已,之后还需要子走别的流程。

  • 前仿,又名功能仿真,是针对RTL的功能验证。信号的跳变是瞬时完成的,所以比较理想,是看看功能上是否正确的。

  • 综合,是将RTL代码生成对应的电路图(门级网表),你可以展开看。

    • 生成文件: 网表文件.v和SDF文件.sdf.
  • 后仿,又称门级仿真,又称动态时序仿真,就是综合以后,在前仿的基础上加入一些约束、单元延时等信息以后,验证设计的时序和功能是否正确,可以说是更贴近现实生活中的电路(上下信号跳变有延迟了)。后仿又分为,综合后仿真和布局布线后仿真。

    • 所需文件:上述的网表文件.v和SDF文件.sdf,综合时所用工艺库db(其内包含对应的标准单元工艺库.v文件),和所用的testbench.
  • 形式验证(Formality),是一种静态的验证手段(动态和静态的区别在后文),根据电路结构静态地判断两个设计在功能上是否等价,常用来判断一个设计在修改前和修改后其功能是否保持一致。不需要testbench,但是必须有一个参照设计和一个待验证的设计,比如说你的verilog的HDL代码和生成的网表,看看两者是否在功能上一致,一致就说明ok。因为形式验证是在数学逻辑上进行比对的,所以贼快!但是它不考虑timing,所以要和STA结合使用。

  • 因为后仿太慢了太慢了, 受不了,所以有些人会不跑后仿,用STA+Formality的形式来替代后仿。但STA只检查边沿timing,Formality只看register和combination的抽象功能,所以呢会有些不完备。后仿在下面三种情况是必要的:异步逻辑设计部分、ATPG向量验证和初始化状态验证。另外,后仿产生的VCD文件还可以做功耗分析。

  • IC时序验证的两种方法:动态时序分析和静态时序分析。

    • 动态时序分析,就是仿真。通过testbench来看时许和功能是否一致,但是不能保证100%的覆盖率,而且需要各种各样的testbench,而且到了门级的仿真会很慢很慢!
    • 静态时序分析(STA),只分析时序不管功能验证,不需要testbench,能100%覆盖所有的路径!但是只能对同步电路进行分析,而不能对异步电路进行时序分析。缺点,是不能完整把所有影响延时的因素给包含进去,所以用STA工具导出关键路径后再门级仿真来确定时序正确。

​ 此处我参考了博客:前仿后仿与形式验证——优快云 fgupupup

2.5 Verilog哪些行为能或不能的概念

​ 在用verilog进行编写逻辑、完成一个project时,一个很坑的地方就在于:一件事情,你能不能做,IDE有时候并不会告诉你、编译器也不会告诉你… 很多合乎程序语言逻辑的行为,其实在当下环节中是不被允许的,但是相关的软件并没有告知你的功能( ̄▽ ̄)"。而这些事情,十足的影响到编写verilog的工作体验,对萌新和小白(我)也非常不友好,前期没有意识到、做足功课,就会导致后期对project进行大改。

​ 举几个在本次project中,我多次热泪盈眶的例子,都是符合verilog语法但是后面才知道又不能做的几件事(不能做是因为后续的环节会失败,但是在撰写前面环节的内容的时候不知道,后期才明白前面写的不对…)。比如说:

  • 阻塞语句和非阻塞语句

  • 同时呢,使用阻塞赋值还会有一些坑,比如“引入信号变化竞争带来的不可预测结果”:

    • 不同的always之间,虽然理论上是并行执行的,但实际上还是要考虑顺序的,因为阻塞赋值会放大信号竞争的情况,不同的always顺序,会导致不同的波形图。如下图:

      • 顺序①:

        always order1
        结果这样的:
        always order1-out
        因为逻辑顺序是先判断 j j j 是否赋值,然后再去修改 i i i. 所以这个波形图里面, j j j 的变化就晚了一个周期。到了第5个cycle才是1.

      • 顺序②:

        always order2

        结果这样的:
        always order2-out

        因为逻辑顺序先修改 i i i,然后判断 j j j 是否赋值. 所以这个波形图里面, j j j 的变化就正常,或说早了一个周期。到了第3个cycle就是1了.

      • 结论: 当有多个always语句块,且有变量之间相互依赖关系时,波形仿真结果会因为竞争出现多种可能的情况,在这个例子中是按照always出现的顺序进行优先执行的(本例子是在vivado下的仿真结果,不同的软件可能结果不同,比如vcs等) 这个问题带来的缺陷是,以后思考逻辑的时候还要去思考这个时序问题如何协调,如何协调每个变量的赋值顺序,以及因为思考不到位出现的一系列隐性bug。

      • 解决方案: 上述问题的根本原因,是因为使用阻塞赋值的实时赋值,从而因为赋值顺序的微观上差异(竞争),导致运算逻辑变得玄学,受always的定义顺序而限定。所以改成使用“非阻塞赋值” 以后,不管是上述那种写法,都将是在第5 cycle以后才会是1(变成了①的波形)!即变量依赖关系全部以滞后一个周期来进行展现!不管是哪种写法,全部变成了图①的结果!

    • 习惯使用非阻塞赋值后,虽然数cycle的时候要注意一下什么时候要晚一周期,但是再也不用担心cycle数跳来跳去的情况了。

  • 用intial语句

    • 写verilog时,每个模块我各自写了个intial进行初始化——✔️——逻辑上没错.
    • 我拿着这些模块去dc(综合生成电路)——❌——想综合电路的话,intial只能在testbench用了,“综合”时并不支持模块内使用intial语句块.
  • 使用二维数组甚至多维数组

    • 因为我使用reg来模拟自己的memory,理所当然的就想到用reg类变量开一坨数组来存放数据。但是在verilog中,一个数据很大概率上本身就是某种意义上的数组,例8位数据 t e m p temp temp 就是reg [7:0] temp; 而占据了多位数据。所以如果我想开一个在C语言中的int数组,开100的大小,那么在verilog中就是一个32位但是列数是100列的二维数组了:reg [7:0] temp [99:0];.
    • 但是!Verilog中的数组变量,不能作为传入传出的端口数据,只能成为中间变量在module内部用用。
    • 其次,Verilog中的数组大小虽然用起来好像是无限的,但是 根据我个人惨痛的教训,在后续dc的compile时(综合阶段),如果你的memory使用的reg数组太大,compile的时间会增加2.5倍!就会卡在compile阶段,让你怀疑是不是代码写错了——即reg数组的电路综合成本是很大的。 但是这些在verilog代码的撰写阶段,并没有sei会告诉你( ̄▽ ̄)"。
    • 为具体说明dc环节对reg数组大小的敏感,我统计了一下不同reg数组大小时,dc的compile阶段增加的时间情况(我只统计了个大概):
      • 对照组:开150长度25位的reg数组,使用dc阶段的compile时间是18s;
      • 开200长度25位的reg数组(1.33倍对照组的memory大小),使用dc阶段的compile时间是31s(1.72倍对照组时间);
      • 开500长度25位的reg数组(3.33倍对照组的memory大小),使用dc阶段的compile时间是185s(10.28倍对照组时间);
      • 开1000长度25位的reg数组(6.67倍对照组的memory大小),使用dc阶段的compile时间是870s(48.33倍对照组时间);
      • 3600长度25位的reg数组,( ̄▽ ̄)"我没试了,估计要3个小时…
    • 上面这个对比,说明reg用的越多,这个dc的compile时间是至少是平方倍数增加呀,所以说verilog撰写过程中,对于数组的使用还是要非常谨慎。不然逻辑上、功能上、波形仿真上都是没有问题的,但是后续环节可能会直接裂开,导致前功尽弃。
    • 同时,还要追加一下出现上述情景的背后原因,不一定对,是我向学长请教后的可能性猜测。当我声明了一个数组,对其初始化操作并不会增加dc的compile时间,但当我使用这个数组的时候,也就是赋值操作的时候数组出现在了等号的右边,对数组寻址的过程会导致上述的compile时间的显著变化结果。这可能是因为数组的索引过程,在dc环节电路映射时,本质上是用MUX进行多选,但是数组的大小会直接影响到每次多选器MUX的大小,就会导致电路中的MUX非常大且复杂,从而影响了dc的效率。所以呢,撰写verilog的时候,一要将时序逻辑功能和相应的电路图心中有联系;二是要理解映射后的电路复杂性是如何的。(当然,第二步要多多积累经验了( ̄▽ ̄)"…)
  • 在多个always块中修改同一个变量

    • 在构思一些功能的时候,我很当然的想到,在一个always块中将某个控制信号变成1,另一个always信号进行监听,随之在下一个cycle将控制信号恢复成0.
    • 很可惜,这个实现思路在逻辑、语法、波形仿真上是对的——✔️
    • 但这个写法是后续的电路综合环节,跑dc时不被允许的——❌——没啥为什么,就是不给你用。所以如果后续要跑综合,要将某变量的修改都放到同一个always里面,用不同的if-else块进行执行。
    • 但是一个always进行变量修改,另一个always中对变量进行读和使用是没有关系滴~

3 Verilog实现思路

3.1 如何进行第一个verilog仿真?

​ 小白如何使用vivado或quartus进行HDL代码的波形仿真呢?这部分内容可以参考我写的这篇博客:

​ 【Chips】如何启动第一个Quartus/Vivado下的Verilog仿真过程 —— 优快云 仰天倀笑

3.2 Project题意与要求

​ 根据Project的要求,实现的小型卷积运算单元要:

  • 运算正确
  • 模块端口限定:
    • 控制端口要含有如开始和结束信号;
    • 卷积的通道数channel和卷积的kernel个数(即输出通道数),比如 卷积是一张 32通道的fmap, 有8个kernel,每个kernel各有32个通道,那么 c h a n n e l N u m b e r = 32 channelNumber=32 channelNumber=32 而 k e r n e l N u m b e r = 8 kernelNumber=8 kernelNumber=8 ;
    • 卷积核的大小固定为 4 × 4 4 \times 4 4×4, 传入的 fmap 的大小也固定为 64 × 64 64 \times 64 64×64, 因此卷积结果大小也固定为 61 × 61 61 \times 61 61×61.
    • 数据端口仅 8个8位的fmap数据线、8个8位的kernel数据线、2个25位的卷积结果输出线;
    • 数据传输控制信号要有:读kernel数据使能、读fmap数据使能和发结果数据使能;
  • testbench的数据传入顺序可以自己定。

conv

3.3 版本一代码设计逻辑

  • 版本一代码特色:

    • 仅8条数据线;
    • 通篇使用阻塞赋值;
    • 无读写控制信号,由counter计数器进行控制逻辑;
    • 卷积结果存储面积以一个完整fmap卷积结果为单位 ( 61 × 61 61 \times 61 61×61);
    • 双存储memory空间轮流使用;
    • 输出数据线只有两根(后面都保持只有两根)。
  • 同步时序的逻辑设计:

    在testbench和卷积模块topModule之间,利用基于时钟周期clk的计数器counter,进行不同时序下的逻辑功能执行,分别是:

    • testbench准备工作好了,就将控制信号置1, 表示卷积可以开始;

    • 版本1的代码审题审错了 ,不知道kernel和fmap的数据线是分开的各8条 ,我以为一共只有8条。所以逻辑写成共用8条数据线,先发送一个kernel,再发送相关的fmap数据。

    • topModule发现卷积开始信号为1时,就开始接收数据并开始卷积计算。

  • topModule计算功能逻辑:

    • 数据线有8根,所以每次能拿8个不同的数据。一个完整的单通道的kernel是 4 × 4 4 \times 4 4×4 大小,所以需要2个cycle就取完一次卷积运算的卷积核。
    • 第一次卷积运算也要取完整的 4 × 4 4 \times 4 4×4 fmap数据,所以也要2个cycle。 而之后,卷积核每一次右移是读入新的4个数据,但是数据总线有8根,有点浪费;所以逻辑改成**:后续每个cycle卷积核在fmap上右移两列,完成2次卷积核运算,**并累加在卷积结果的memory上。
    • 由上述提到,每个cycle,我们都会执行两次卷积CONV,因此整个电路中拥有两个卷积模块,并行运行。
    • 当一行卷积结束以后,卷积进入下一行的开头进行运算,此时又要读取前 4 × 4 4 \times 4 4×4 大小的fmap数据,需要2个cycle,此处要停一下,并不会计算卷积。
    • 当所有行卷积都结束以后,说明一个channel的卷积结果结束,进行下一个channe的卷积运算。那么就需要重新读取卷积核的内容,也就是下一个channel的kernel的数据,又需要2个cycle读取 4 × 4 4 \times 4 4×4 的kernel数据。
    • 当所有channel卷积结果都累加在memory上以后,就将当前的memory输出,即当前kernel的卷积结果输出;并开始从下一个kernel的首个channel开始读取卷积核以及fmap的数据。
    • 由于输出卷积结果的时候,是在当前fmap的多个channnel的卷积值都累加完毕以后才进行的,输出结果的时候不能memory就不能进行下一个kernel的计算了,会导致计算资源的闲置。因此,决定采用双 61 × 61 61 \times 61 61×61 大小的memory空间,一个用于输出卷积结果,一个用于卷积计算,实现并行化。
    • 当最后一个kernel的卷积结果都结束时,将模块结束使能信号置1.
    • 卷积累加后的ReLU函数在输出时用if句追加。
  • 时钟周期计数器的安排:

    • 由于全project的模块执行逻辑由计数器决定,因此计数器的各数值对应的运行逻辑要提前规划清楚,来确定卷积模块的执行行为与数据传送逻辑。在整个project中,一共有cycleCounter、rowCounter、channelCounter、kernelCounter计数器,分别统计卷积过程中当前的相对周期数、行数、通道数、kernel数。其中cycleCouter到达一定数值,就会rowCounter增加并复位cycleCounter;rowCounter到达一定数值就会使channelCounter增加并复位rowCounter;以此类推。

    • 在每个channel的第一行( r o w C o u n t e r = = 0 rowCounter==0 rowCounter==0)时,要读取kernel数据。

    • 在每个kernel的最后一个channel运算结束时,输出当前kernel的卷积结果。并复位所有memeory的值及相关信号。

    • 对于cycleCounter的运行逻辑如下:(数据线8根)

      • cycleCounter: 0~1, read 4 × 4 4 \times 4 4×4 kernel数据.
      • cycleCounter: 2-3, read 4 × 4 4 \times 4 4×4 fmap数据. 计算一次卷积CONV.(当前行的首个 4 × 4 4 \times 4 4×4 fmap数据)
      • cycleCounter: 4-33, read 两列 4 × 1 4 \times 1 4×1 的fmap数据. 计算两次卷积CONV. (重复30次,则单行卷积运算结束)
      • 进入下一行,并复位 c y c l e C o u n t e r = 2 cycleCounter=2 cycleCounter=2 (并不是0,因为kernel数据不需要重复读取),且增加rowCounter进入下一行,后续逻辑类似。
      • 对于memory翻转切换使用的逻辑,以及卷积运算单元的结果取回逻辑(需要一个cycle计算,所以取回会滞后一个cycle),在此略过不谈(单位卷积运算单元结果取回逻辑和版本二有少量区别,与版本二不同,版本一比较辣鸡)。
  • 出现的一些基础问题:

    • 对于寄存器如memory的初始化行为使用不成熟。在此版本中采用的是使用 intial 语句进行初始化。实际上这样的方式在功能级是正确的,但是在dc综合阶段是不被允许的,因此后续版本有优化。
    • 寄存器使用大、电路也贼大。
      • ①是我们的卷积计算保存的是 61 × 61 61 \times 61 61×61 的寄存器数组,对于电路而言太大了,更何况电路要基于数组下标生成索引用的MUX电路,会导致电路更大而大;
      • ②因为此版本中使用了两个memory实现计算与结果输出的并行,那就会导致寄存器数量翻倍,恶化了情况。
    • 代码复杂。
      • ①第一次写verilog,对于always语句块以及相关逻辑不够清楚,使得写出来的代码又臭又长;
      • ②代码实现逻辑基于计数器counter,好几个不同职责的counter在不同阶段复位为0的过程显得极其混乱。
      • ③模块使用了双卷积计算单元convModule,而单cycle要执行两次卷积的数据移位过程,增加代码复杂性。而卷积计算单元的结果要在下个周期取得,因此卷积计算单元的取回也需要额外的逻辑处理。
      • ④要实现上述的双寄存器翻转切换使用的逻辑,加入的判断逻辑会进一步增加复杂量;
  • 程序结果:

    • 功能正确——✔️
    • 不可综合——❌

3.4 版本二代码优化逻辑

  • 版本二代码特色:

    • 更加符合题意,补全了 读写控制信号;
    • 补全数据线:8条fmap数据线以及8条kernel数据线;
    • 通篇使用非阻塞赋值;
    • 卷积结果存储memory优化,从双memory切换使用,改成仅使用单 61 × 61 61 \times 61 61×61大小;
  • 修改代码的初衷:

    • 更符合题目要求(控制信号与数据线的数目);
    • 版本一的代码跑不了dc(综合)。因为我使用的阻塞赋值,所以需要优化。(当然,后续还发现memory使用过大)。
  • 功能逻辑的修改与调整:

    • 首先,为了增强各模块的独立性,也是题目的要求,需要顶层topModule模块需要向testbench”接收数据“的控制型号,化”被动型接受数据“为‘主动性接收数据”的形式。同时,在发送输出卷积结果时,也需要将“发送数据”的使能信号置1.
    • 可综合verilog代码要求,是时序逻辑代码用非阻塞赋值,而组合逻辑代码使用阻塞赋值。我写的verilog要想能综合成电路,那当然得符合时序逻辑。因此,版本一通篇的阻塞赋值代码是没办法综合的。我之前想当然决定“功能正确就能产生对应电路”的想法明显是错误的。
    • 由于题目要求,fmap的数据线与kernel数据线是独立开来的各8条。之前自己的审题不清,给自己带来了额外的修改工作量,以及对计数器的时序逻辑的大修改,也是没办法的事情。
    • 第二次审视自己的memory使用代码,显得非常多余。为什么要等待 61 × 61 61 \times 61 61×61 卷积值累加结果都计算完毕了才开始传输结果呢?这样确实需要两块memory才能实现计算与结果输出的并行。但是若在最后一个channel的卷积值累加完毕就立马进行输出,实现累加与输出的并行,那么当 61 × 61 61 \times 61 61×61 的卷积值累加结果都计算完时,前 61 × 60 + 60 61 \times 60 + 60 61×60+60 的卷积结果也都传输完毕了,就不需要两个memory了,节约了电路空间也简化了代码逻辑。( ̄▽ ̄)"
  • 代码修改的实现细节:

    • 增加了8条kernel数据线,使得计数器的counter安排上出现了修改,并微调了单位卷积运算结果的获取逻辑。
    • 将代码修改为非阻塞赋值,使代码中对于counter的修改会在下个cycle中得到体现,而当前cycle使用的counter值并不会变,这在并行的always语句块中对同counter的访问时需要强烈注意。
    • 由于将代码修改为非阻塞赋值,对于控制信号的修改,testbench也只能在下个时钟上升沿检测到并采取行动,因此相关控制信号的置位与复位需要提前进行!
    • 对于cycleCounter的运行逻辑如下:(数据线8根以及kernel数据线8根)
      • cycleCounter: 0~1, read 4 × 4 4 \times 4 4×4 kernel数据. 也同时 read 4 × 4 4 \times 4 4×4 fmap数据. 并计算一次卷积CONV.(当前行的首个 4 × 4 4 \times 4 4×4 fmap数据)
      • cycleCounter: 2, 取上次卷积结果CONV,并read 两列 4 × 1 4 \times 1 4×1 的fmap数据, 计算此两次卷积CONV.
      • cycleCounter: 3-31, 取上两次卷积结果CONV,并read 两列 4 × 1 4 \times 1 4×1 的fmap数据. 计算两次卷积CONV. (重复29次)关读取fmap数据信号(因为下cycle不计算只负责回收此行最后卷积结果,故关信号防止漏了数据。而要切记:此处为非阻塞赋值,下cycle才起效)
      • cycleCounter: 32, 取上两次卷积结果CONV.
      • cycleCounter: 33, 开读取fmap数据信号(切记:非阻塞赋值,下cycle起效;其实是可以 c y c l e C o u n t e r = 32 cycleCounter=32 cycleCounter=32合并的,但是写错了就懒得改了…( ̄▽ ̄)")
      • 进入下一行,并复位 c y c l e C o u n t e r = 0 cycleCounter=0 cycleCounter=0 (此处是0,因为fmap数据已经与kernel数据并行读取了,而kernel数据是否读取是由控制信号控制的而不仅仅是cycleCounter了,可以放心了),且增加rowCounter进入下一行,后续逻辑类似。
      • 对于输出结果的逻辑部分,即检测当前channelCounter是否是最后一个通道,如果是,就将控制信号打开,并每个cycle累加完卷积结果就立马输出,这样就不需要两个memory了。
      • kernel数据的读取使能信号以及其他控制信号的何时置1与复位的逻辑类似,在此略。不然写得就有点乱了。仅写了 fmap数据的读取信号的置1与复位逻辑供理解。核心特点是:要提前置1或复位,因为非阻塞赋值和时钟上升沿的关系,testbench下个cycle才能检测到。
  • 程序结果:

    • 功能正确——✔️
    • 依然不可综合——❌——( ̄▽ ̄)"

3.5 版本三代码优化逻辑

  • 版本三代码特色:

    • 一切为代码可综合服务!
    • 卷积结果存储memory再次优化,从reg类型的 61 × 61 61 \times 61 61×61大小的memory优化成 61 61 61 大小的memory;
    • 将always逻辑优化;
    • 用testbench传入的初始控制信号来代替intial进行模块的初始化工作
    • 单元卷积计算模块,增加上一个卷积累加结果作为进位端口,省去累加的cycle。
  • 修改代码的初衷:

    • 正如我上文的VLSI基本概念篇幅内所言,电路是否可综合真的和verilog代码功能是否正确没关系,所以即使上述代码版本都正确,依然无法成功用dc将verilog进行综合。
    • 有些不可综合的原因dc是会告诉你的,譬如:
    • 但最骚的是,有些不可综合的原因dc是不会告诉你的。譬如,我的版本二代码完成上述修改以后,程序在进去dc后就一直处于compile阶段卡死了,30分钟后都没反应。于是我就开始debug,将代码全删去,再一行一行加进来,看看加到哪一行的时候dc会卡死,说明这行verilog代码有问题。 功夫不负有心人( ̄▽ ̄)",我发现是我的memory开太大了…
      • 大伙儿可以回到上述的 【2.5 Verilog哪些行为能或不能的概念】中提到的,“dc对memory” 敏感度。 也就是说是我们的memory使用太大了,导致compile的时间预计要3个小时~( ̄▽ ̄)",这就很离谱,小作业要compile这么久谁顶得住?既然 61 × 61 61 \times 61 61×61 的memory太大了,就必须想办法精简!
  • 功能逻辑的修改与调整:

    • 一,代码中不能添加intial模块。但是我又需要初始化工作:如控制信号的初始化,memory的初始化等等。如何实现?我们这个卷积核的project的启动是根据testbench传入的开始使能信号触发的,因此,我们可以让开始信号为0的时候作为判断条件写一个always进行初始化,代替intial语句块,同时让testbench保持开始信号为0一段时间,确保模块的初始化工作能够完成。

    • 二,优化存储空间。

      • 原来的卷积运算逻辑是:以完整的fmap为一个单位进行卷积计算并累加——对于 61 × 61 61 \times 61 61×61 的所有卷积结果,把所有channel的卷积值累加上去,输出掉;再下一个kernel。

      • 现在可以更改为:以fmap的一行为单位进行卷积计算并累加——对于每一行的 61 61 61 个的卷积结果,把所有channel的卷积值累加上去,输出掉;再下一行fmap;所有行结束了再下一个kernel。

    • 三,进一步提升卷积结果取回速度。之前的逻辑是:卷积单元运算,运算结果取回,运算结果累加并发送;可以对卷积计算单元进行补充,将之前的卷积结果作为“来自低位的进位”,就可以实现类似“全加器”比“半加器”多出来的优势,能够直接再卷积单元里把累加过程给完成了,节省一个cycle。

  • 代码修改的实现细节:

    • cycleCounter的逻辑细节基本和版本二一致,但是将在当前channel的行尾卷积完成后,将“进入下一行”变成“进入下一个channel”,并复位 c y c l e C o u n t e r = 0 cycleCounter=0 cycleCounter=0; 只有当前channel是最后一个通道,完成当前行的卷积结果输出后,才会增加rowCounter进入下一行,其他逻辑大致不变。
    • 其他逻辑修改在此不赘述。
  • 程序结果:

    • 功能正确——✔️
    • 终于 可综合——✔️——( ̄▽ ̄)" Nice!

4 运行性能

​ 虽然verilog迭代了好几个版本,但是第三版的verilog代码是写的相对简洁而干净, 我很满意。没有用什么switch和多余的if-else结构,所以dc也没有生成冗余的latch等电路。虽然功耗、性能一般,但是我个人觉得还行~( ̄▽ ̄)"

  • 总计算周期数

    • 总计算周期数 = 单行卷积数×channel数×行数×kernel数,(需额外1cycle 以模块初始化,未算入)
      • 8 channels 8 kernels: 34 × 8 × 61 × 8 = 132736 34 \times 8 \times 61 \times 8 = 132736 34×8×61×8=132736 cycles
      • 32 channels 32 kernels: 34 × 32 × 61 × 32 = 2123776 34 \times 32 \times 61 \times 32 = 2123776 34×32×61×32=2123776 cycles
  • Area 面积

Area

  • Power 功耗

Power

  • Timing 时延

Timing

关键路径所需的时间4.97ns.

  • 计算(A x P x {1/f} x N) PPA

    74683 × 6.6635 × 4.5 n s × 132736 = 2.973 × 1 0 8 ( μ m 2 × m W / M H z ) 74683 \times 6.6635 \times 4.5ns \times 132736 = 2.973 \times 10^8 (\mu m^2 \times mW / MHz) 74683×6.6635×4.5ns×132736=2.973×108(μm2×mW/MHz)

  • Formality 形式验证

Formality

5 dc(综合)后、 综合后仿真的结果与感悟

​ 虽然成功跑完综合,但是后仿就没有成功了。没有debug,不知道问题出在哪里。也许是因为testbench设置的时间不够…整个程序我个人感悟最大的是verilog代码的迭代部分,而后续dc综合、dc后仿真以及形式验证是伙伴捣鼓出来的,理解不能( ̄▽ ̄)",抱了一波大腿~

​ dc的界面好丑啊…当时看到的quartus觉得好丑,看到vivado眼前一亮,看到dc又觉得好丑… 也许这就是垄断吧,希望日后能有国产的相关产业链,软件能更加人性化的同时也能更好看一点~( ̄▽ ̄)" 不能作为程序员就对美感失去追求,是吧~

6 Code

​ 时隔好久,神奇的发现自己没上传代码… ( ̄▽ ̄)"
​ 代码见此处: 21-06-VLSI-CONV_RTL ,第一次写verilog,技术含量很有限,可以给和我一样的 verilog 入门小白做一个参照~

标准单元工艺库(TSMC 90nm)文件详解 以TSMC 90nm标准单元工艺库为例,详解工艺库的结构与内容。 阅读详情

相关推荐

Linux环境下EDA软件的使用——数字IC设计DC综合篇

Linux环境下EDA软件使用教程———数字芯片设计DC综合篇 一、综合的基本概念 1.1综合概述   通常我们所说的综合指的是逻辑综合。逻辑综合就是使用综合工具将较高层次的RTL级描述转换为基于目标工艺库和特定约束条件的门级网表的过程。 1.2综合的流程   综合包括转转译(Translation)、映射(Mapping)、优化(Optimization)三个过程。   转译是指把电路的HDL描述转化为与工艺无关的功能模块的逻辑电路的过程。这个过程不包括任何重组和优化,也与制造工艺没有直接联系。   映射

weixin_45950169的博客 9998

Vivado:信道编码卷积编码和RS编码IP核

在利用FPGA进行通信系统编写的过程中,有时需要进行信道编码的过程,如常用的卷积码和RS码,这里两种编码类型是一种常用的信道编码过程。 卷积编码对应的是维特比译码过程,rs编码对应rs译码,这个译码的过程是相对比较复杂的,在验证的过程中如果纯代码的形式完成译码的过程,需要下一点功夫,好在vivado提供了卷积编码,维特比译码,rs编译码的IP核供我们使用。 正好提供了对应的编码码的过程,参照这IP核的使用手册,可以方便进行设计和实现,但是有的license没有破解该IP核,只是破解了vivado,这

qudunan6468的博客 5298

【基于zynq的卷积神经网络加速器设计】(一)熟悉vivado和fpga开发流程:使用Vivado硬件调试烧写hello-world led闪烁程序实现及vivado软件仿真

HIGHLIGHT: vivado设计流程: note: 分析与综合 和 约束输入 可以调换顺序 【基于zynq的卷积神经网络加速器设计】(一)熟悉vivado和fpga开发流程:使用Vivado硬件调试烧写hello-world led闪烁程序实现及vivado软件仿真一、硬件调试(一)新建工程(二)设计输入(三)分析(四)约束输入(五)综合、实现、生成比特流二、软件仿真(一)步骤(二)仿真界面指导 一、硬件调试 (一)新建工程 new project next 编辑,然后next 勾选创建

点赞的都能无bug通过! 3833

python 中级遍1 - __init__初始化方法

在 Python 的类中,有一种特殊的方法——初始化方法。它的格式是 def init(self):,方法名由 init(initialize 的缩写,初始化的意思)加左右两边的 双下划线 组成。 1.__init__初始化方法 当使用 类名()创建对象时,对自动执行以下操作: 1.为对象在内存中 分配空间 ---- 创建对象 2.为对象的属性 设置初始值 ---- 初始方法(init) 这个 初始化方法 就是 init 方法 init 是对象的* 内置方法* init 方法是 专门 用来定义一个类

伟爵社的博客 1552

基于Verilog搭建一个卷积运算单元的简单实现

基于Verilog实现卷积神的运算需要,有3个要素,图片信息、滤波器权值信息和大量的乘加运算,一个基本的卷积运算过程如图1所示,本博客是在前文(1. Vivado简单双端口RAM 使用,问题探析和基于verilog的CNN搭建缓存图片数据浅析)分析的基础上,系统的说明具体的实现过程,主要包括代码分析和仿真实现,旨在自我学习记录。为了加深理解,且便于仿真,本实验模拟了一个大小为5×5大小的图片结和2×2的滤波器,注意:本实验没有考虑偏置项。图1 卷积的实现过程图2 图片(左)和滤波器(右)

安静到无声 5303

硬件接受单元测试周期:8.9 CHIPS分配

验收单元测试周期 在此作业中,您将结合使用“接受度”和“单元测试”以及Cucumber和RSpec工具,向RottenPotatoes添加“查找具有相同导演的电影”功能。 (如果您不是直接在GitHub上阅读此书,则需要临时。) 要开始:在此仓库中,单击上方的绿色“使用此模板”按钮以在您自己的GitHub帐户中创建该仓库的干净副本。 学习目标 完成此任务后,您应该能够: 创建并运行简单的Cucumber方案以测试新功能 使用RSpec创建单元测试,以推动创建使Cucumber场景通过的应用程序代码 由于新功能经常涉及数据库架构,模型,视图和控制器,因此了解在何处修改Rails应用以实现新功能的各个部分 简介和设置 将此模板存储库的副本克隆到您的开发环境中(请勿克隆该参考存储库,否则您将无法进行更改); git clone https://github.com/YOUR_GITHUB

Linux Kernel 2:用户空间的初始化

上篇我们知道,kernel初始化后将启动init进程,那么这个进程将干些什么呢?除此之外,kernel还需要做些什么事情呢?(想想文件系统、根存储设备是在什么时候初始化的呢?) 先从文件系统初始化说起。以前一直不明白,有了kernel为何还需要一个文件系统?经过反复琢磨,明白一个道理,kernel加载到内存后,kernel运行起来是没有问题的,但是如果没有Root FS,就好像PC上没有硬盘.....

jandroid 314

实测对比:DINOv2、CLIP、ResNet在图像相似度任务上谁更强?(含Python代码与避坑指南)

本文通过实测对比DINOv2、CLIP和ResNet在图像相似度任务中的表现,提供Python代码实现与避坑指南。实验结果显示,DINOv2在精度上表现最佳,CLIP在多模态场景中优势明显,而ResNet则在计算效率上更优。文章还针对不同业务场景给出了具体选型建议,帮助开发者在图像搜索和版权检测等项目中做出更明智的技术决策。

weixin_30588381的博客 285

VLSI 课程介绍

VLSI 课程介绍 ##又到了年底选课的时候,想想一年前刚来USC的时候,我是看着刘叔的专业和选课介绍长大的,这一年多以来,承蒙各位学长学姐的厚爱,在美国的学习和生活中少走了很多弯路。如今刘叔的第二个master学位都快毕业,而新一届的学弟学妹们肯定也会面临很多选课方面的问题,USC EE VLSI的课程设置也发生了一些变化,因此花了点时间,在刘叔原文的基础上加入了一些新的内容【和吐槽】,希望能帮...

weixin_44241947的博客 8253

【参数不确定】敏感性分析(sensitivity analysis)

【运筹学】灵敏性分析

羊肉蛋花汤的博客 14万+

基于立创·天猛星MSPM0G3507的简易PID电机控制入门项目实战

本文详细介绍了基于立创·天猛星MSPM0G3507开发板的简易PID电机控制入门项目实战。从硬件选型、电路设计、PCB布局,到软件编程中的编码器读取、PID算法实现与调参,手把手教你完成一个带屏幕显示的电机定速与定距控制项目,是学习嵌入式开发和经典PID控制算法的绝佳实践。

weixin_42596011的博客 509

vivado 和 modesim 联合仿真&&快速修改重仿

https://blog.youkuaiyun.com/wordwarwordwar/article/details/53428514 1 编译联合仿真库 tool →compile_simulation_libraries ,选择simulator 为 modelsim simulator,填写好 compiled library location(联合库存放地址),simulator executable path (modelsim.exe 地址) 2 settin...

gaoxcv的博客 6334

转录组分析中的Fastp和FastQC:如何快速检测数据质量并优化流程

本文深入探讨了转录组分析中数据质量控制的核心流程,重点介绍了Fastp和FastQC两款工具的组合应用。FastQC负责对原始测序数据进行全面质量评估,生成可视化报告;Fastp则能一站式完成接头切除、低质量序列过滤、长度筛选等关键质控步骤。文章提供了详细的参数解读、实战脚本和自动化流程构建指南,帮助研究人员快速检测数据质量并优化预处理流程,为下游的差异表达等分析奠定可靠基础。

weixin_28452161的博客 178

FPGA数字信号处理(27)卷积编码器与Viterbi译码器设计

卷积编码与译码 信号在信道间传输主要会受到三个方面的影响: 信道本身对信号产生衰落,这是由于信道本身的频率响应特性就不理想,对信号造成破坏; 信道中的各种噪声,叠加在信号上改变了信号的幅度、相位、频率,造成解调错误; 多径效应,信号在传输过程中的反射、折射、沿不同路径传播造成的叠加效应。 通常会采用信道编码,在发送端插入一些冗余码元,接收端利用这些冗余码元检测并纠正信号在传输过程中产生的错误。...

FPGADesigner的博客 1万+

别再只玩积木了!用乐高EV3和官方软件,手把手教你做个能巡线的小车(附完整代码)

本文详细介绍了如何利用乐高EV3套件和官方软件制作智能巡线机器人,从硬件组装到软件编程全面覆盖。通过传感器原理讲解、控制算法调试等工程化细节,帮助读者掌握机器人开发的核心理念,特别适合STEAM教育和硬件编程爱好者实践。

weixin_30776273的博客 449

Plant Simulation 快速建模与布局(3)——自动生成的对象 代码“安全”删除管理

《PlantSimulation快速布局建模课程》教授利用SimTalk语言实现高效建模的技术要点,包括批量对象创建、阵列布局、属性管理及自动连接功能。重点讲解对象安全删除机制,通过existsObject检查对象存在性后使用deleteObject方法删除,避免程序报错。课程提供完整代码示例,展示如何通过循环结构实现矩阵布局对象的创建与安全删除,确保代码健壮性,显著提升建模效率。

qq_31964455的博客 116

卷积编码verilog实现

完成(2,1,7)编码方式 多项式为[133,171],也可以完成(2,1,5)编码方式多项式[23,35]的verilog实现对应代码中的注释的位置。相应的译码器也写已写完,后续。。。。。。。。。。。。。相关的卷积编码的基础知识请参看https://blog.youkuaiyun.com/u011639609/article/details/51476278###;`timescale 1ns / 1ps/...

weixin_39573321的博客 6529

深度学习100例-卷积神经网络(CNN)彩色图片分类 | 第2天

文章目录前期工作设置GPU导入数据标准化可视化构建CNN网络编译训练模型预测模型评估 前期工作 设置GPU 如果使用的是CPU可以忽略这步 import tensorflow as tf gpus = tf.config.list_physical_devices("GPU") if gpus: gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量

“365天深度学习训练营”报名进行中~ 7853

【心随意动】研一春季下学期小结

​ 前半学期感受:练就半天一份paper reading的能力与1天完成一份deadline的能力。 ​ 学期中感受:练就1天备考然后如愿考出一坨屎的能力。 ​ 后半学期感受:学习迭代verilog代码的能力。 ​ 学期末感受:练就死猪不怕开水烫而超脱世俗的能力。 ​ 研一感受:经过一年的学习,努力的从一个放不下卷面、想有高积点分的学习废物,掰成淡泊成绩求过即可的纯废物( ̄▽ ̄)"。 ​ 后面简单记录一下各课程的粗糙学习体验。 VLSI流程 课程概述:6学分的课,前2/3学期的4学分的讲座+理论,后1/

Hide_in_Code 544

【实战】LLaMA-Factory 如何做 LLM 的多轮对话微调

LLaMA-Factory 作为一个广受欢迎、易于使用的 LLM 微调框架,为开发者提供了便捷的途径来针对特定任务(包括多轮对话)优化模型。本文将深入探讨 LLaMA-Factory 如何支持多轮对话微调,涵盖其基本概念、数据准备、核心机制、评估方法等。简单来说,多轮对话是指包含两个或更多轮次(Turn)的交互过程,其中每一轮通常包含用户的一个输入和模型的一个输出。后续轮次的回应通常依赖于前面轮次的对话内容。模型需要理解并记忆对话历史。

kakaZhui的博客 1309

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

结构虽简单,但性能强悍——3个小型卷积神经网络简介

人工智能、深度学习太火了,火到哪一个领域都有讨论的声音。稍微对深度学习有所了解的人们,尤其是对于计算机视觉领域有所涉猎的人们,当一听到深度学习、卷积神经网络时,脑海里不由得自动脑补这样一幅画面——一台满和负载嗡嗡响的GPU服务器、一台满屏代码不断跳跃的显示屏以及坐在电脑前不断尝试不同卷积神经网络模型的科研人员。这幅场景在各个实验室不断的重复...

weixin_33728708的博客 399

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

卷积函数的FPGA实现(七)vivado系统集成与烧录

背景:已经输出IPcore为RTL,现在需要将相应的IPcore集成为系统并生成相应驱动。 目的:集成IPcore为系统,并生成驱动。 参考相关文档: IPcore调用DDR https://blog.youkuaiyun.com/weixin_36474809/article/details/84942607 ARM调用DDR https://blog.youkuaiyun.com/weixin_3647480...

祥瑞的技术博客 2291

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

小型数据集卷积神经网络CNN训练策略

小型数据集卷积神经网络CNN训练策略训练一个小型的模型使用预训练的网络做特征提取对预训练的网络进行微调 在很多场景下,我们都只有几千甚至几百个样本,而要使用这样的小样本来训练一个cnn网络又避免过拟合,需要采用一些策略,目前主要采用的策略主要有以下三种: 训练一个小型的模型 使用预训练的网络做特征提取 对预训练的网络进行微调 训练一个小型的模型 这种方案主要适用的场景是用于比较简单的任务...

lynnchan的专栏 1万+

美国防部CHIPS项目拟研发微型、模块化“芯片粒子”

据外媒报道,日前,美国国防部高级研究计划局(DARPA)推出了一个叫做CHIPS的新项目,全称Common Heterogenous Integration and Intellectual Property Reuse Strategies Program。听起来是不是又长又拗口?实际上,这个项目的研究跟名字一样,也是非常复杂的,DARPA希望研发出微...

weixin_34295316的博客 307

【芯片学习】X86 CPU 发展历史与分析——1971~2020——明白Intel架构的变迁

近日由于需要,我对x86的CPU架构发展历史进行了调研与学习。目前主流的x86架构,其先进的技术代表厂家就是Intel与AMD,它们相爱相杀的故事广流于家家户户。但是我整理Intel的相关CPU发展就花了2、3个星期(期间还有别的事情),于是AMD的相关产品还没来得及整理与写入。关于Intel的芯片发展过程,相关的整理网站其实还不少,但大多数都停在2011年(因为也是那个时候写的),也就是截止到Intel Core架构之前,而后续的介绍文章都很稀碎。我借鉴了很多很多诸如这样的文章与维基百科,主要参考网站都放

Hide_in_Code 1万+
上一篇: 【Chips】如何启动第一个Quartus/Vivado下的Verilog仿真过程
下一篇: 【心随意动】研一春季下学期小结
仰天倀笑
博客等级 码龄10年 313粉丝 124原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值