当前位置:网站首页>SOC验证环境的启动方式
SOC验证环境的启动方式
2022-06-25 04:01:00 【Alfred.HOO】
SOC验证环境一千家公司有一千家公司的做法。那么一个优秀的SOC验证环境应该具备哪些功能呢?
首先是SOC验证环境支持C和SV两种下激励的方式。
通过C code启动SOC环境是怎么启动的呢?这里涉及到CPU如何boot,对此很多转行的同学可能很难理解,在这里和大家做个简单的介绍。
我们知道CPU执行的是指令和数据,冯诺依曼结构是将指令和数据合并在一起存储,如8086。
哈弗结构是将指令和数据分开存储,如ARM系列。
无论是哪种结构CPU都是通过指令实现程序跳转或者数据读写。什么是指令?以RISCV为例,其指令格式如下
我们以简单的加法运算为例,比如我想做一个 rd= rs1+rs2 这样一个寄存器加法运算,伪代码就是 add rd,rs1,rs2 。通过上述RISCV的指令格式应该就是
funct7,funct3,opcode又是什么呢?通过查询 riscv 手册可以查到以下结果。
由此我们组成一个简单的加法的操作指令,CPU拿到这个操作指令之后就可以进行寄存器的加法运算。
事实上除了上述的寄存器操作指令,CPU还支持其他控制指令和数据处理指令。以riscv的标准指令集为例,其分别有以下6种类型。
R-format for register-register arithmetic/logical operations
I-format for register-immediate arith/logical operations and loads
S-format for stores
B-format for branches
U-format for 20-bit upper immediate instructions
J-format for jumps
大部分我们SOC对寄存器,memory的访问,数据的处理可以分解成上述6种类型的指令。由于底层的机器指令晦涩难懂,在存储访问和数据处理的时候,人们更倾向于用高级语言C来进行描述。C又是如何最终转换成机器指令送给CPU的呢?这就要讲到编译的过程。
从C语言到机器语言总共要经过3步,分别是编译,汇编和链接。
C编译成机器码要通过预处理,编译,汇编,链接四个步骤。这四个步骤由谁做的呢?答案是编译器。
编译器做的工作类似我们IC行业里面的综合。在IC设计中,门级电路特别复杂,特别是当逻辑门数比较多时,通过直接设计逻辑电路变得非常困难。由此产生了verilog。我们通过Verilog描述电路的功能,然后通过DC进行综合,让我们很方便的进行大规模数字逻辑电路的设计。
在软件层面,编译器做得事情和综合类似,编译器的编译的过程也分前端和后端。编译器的前端主要完成分析阶段读取源程序,这其中包括词法、语法和语法检查,生成中间源代码和符合表等。编译器的后端,综合阶段通过中间源代码表示和符号表生成目标程序。具体的编译器各个阶段做得事情,这里不做详细介绍,感兴趣的粉丝可以自己找资料学习。
C语言的编译器有很多种,在我们芯片行业,主要有GCC和LLVM。下面框图简单的描述了一个CPU编译器组成。
我们都知道CPU的执行效率和CPU的频率及指令流水处理的效率有关系。很少人知道CPU的执行效率还和编译器有很大关系。编译器对C代码进行优化可以大大的缩减代码量,提高执行效率。国内有不少公司专门做这方面的研究。
上面我们讲完编译器的相关知识,在集成SOC环境的时候,我们需要集成工具链用于编译C语言生成机器码。
机器码生成了,该怎么输入给CPU使用呢?
介绍CPU boot的过程。对此我们先看下一个哈弗结构的CPU的框架。
CPU通过指令总线从指令存储器里面读取指令进行操作,如果需要数据则从数据存储器里面读取数据处理。下面是一组真实的指令总线和数据总线。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QrstlNoJ-1656071001228)(https://img2022.cnblogs.com/blog/2601779/202206/2601779-20220624183634859-745978447.png)]
从上面的代码中可以看到,指令总线和数据总线都有地址和数据信号以及一些握手信号。在CPU reset之后,CPU会主动通过指令总线去某个特定的位置读取机器码。下图是CPU接口上一组输入信号,boot_addr_i 就是指令CPU reset 之后从哪里开始读代码。
由此我们可以通过将编译好的机器码放在mem里面,然后设置boot_addr_i 指定到该mem的位置,实现一个最小的CPU boot系统。
实际芯片的模块比这个最小系统复杂很多。存储介质上分为ROM,SRAM和Flash,一些大的芯片还有DDR等。
看到这些存储模块,有些人想了,是不是可以把机器码放在里面?这就要讲到boot的几种模式。
第一种将代码放在SRAM里面直接启动。具体操作的手段是将CPU的boot_addr_i 指定到SRAM的位置,在仿真开始的时候将code load 进 SRAM memory里面即可实现 从SRAM里面boot。SRAM boot的一个问题是每次关电,SRAM都会被清除,无法保存code。
第二种是将代码放在ROM里面,但是很不幸,ROM的空间非常小,如果将整段代码放在ROM里面不太现实。所以很多人将ROM 用作存储引导代码,即bootrom code。然后由bootrom code 跳转到SRAM或者flash执行的位置。
第三种是将代码放在Flash里面。通过bootrom code 从flash里面将代码搬到SRAM里面执行。另外一种方式是通过bootrom code 去配置flash 进入XIP状态,然后CPU直接从flash里面读取指令进行执行。通过这类方式boot的好处是掉电代码没有遗失,是真正产品应用的方式。
上面是三种在我们SOC验证中经常用到的boot方式,真实芯片的boot比这个复杂得多。
以ARM为例,系统boot会被分成三个步骤。
第一级bootloader:引导加载程序,即bootrom code,会选择哪种方式启动系统(EMMC,UART,SPI…)。第一级bootloader执行完之后会跳转到第二级bootloader。
第二级bootloader:用于硬件的初始化,比如初始化时钟,中断,看门狗等,这段代码放在SRAM中执行。执行完这个会跳到第三级bootloader。
第三级bootloader: 这个才是我们C代码的入口,也就是我们写的main函数在这里开始执行。
bootrom是一门比较复杂的系统工程,在真正产品应用中还需要考虑可靠性和安全性。但是对我们SOC验证来说,在系统里面集成最基础的三种boot方式即可满足大部分验证需求。
上期有朋友问我有没有简单的SOC验证环境,答案是有,在公众号后台回复"SOC验证"可获得一个最小系统的SOC验证环境。
https://github.com/openhwgroup/cv32e40p
上次说到CPU的boot,今天说说SOC环境的另外一种启动方式。用C启动SOC验证环境有几个问题。
一是CPU boot过程比较慢,每次仿真前都需要很长的一段初始化时间。
二是IP验证环境的测试用例无法直接复用到SOC环境里面。
对于小型的芯片用C做仿真还可以,但是对于大型的SOC芯片,用C做仿真效率有点低。基于上面两处不便,我们考虑能否用UVM直接接管CPU,然后通过SV/C直接下激励。
上述方法是可行的,为了让SOC环境跑的更快,用相应的BUS的agent接管 CPU的总线,将CPU bypass过去,然后通过UVM的环境调用不同agent的driver,实现给不同模块激励。
如上图所示,右边是一个SOC,CPU通过总线访问SPI,USB,PCIE,DDR,I2C等。在SOC验证环境里面,通过usb_agt 接管去往USB的总线,用pcie_agt接管去往PCIE的总线。当然也可以用chip_agt 接管CPU出来的总线。总而言之做法就是通过UVM去接管系统的总线。
这样我们可以bypass boot的过程,并且还可以实现IP的验证环境在SOC验证环境中复用。如果我们对C代码进行一些封装,还可以通过C访问SV从而实现C test在这类环境的复用。
通过UVM接管CPU,bypass boot的过程,我们仿真的速度可以加快不少,但是SOC还是非常大,在编译和仿真的时候,会消耗很多时间和内存。为了加速,我们将不用的module用empty的module代替,只保留接口信息,模块内部不实现,这样可以大大减少SOC环境的逻辑单元数和信号的翻转率,提升编译和仿真速度。
上图中,我们在测USB的时候会用到DDR,但是SPI,PCIE和I2C都不会用到,因此我们将这些模块用空的module代替。采用这些手段,这个SOC的验证环境就可以跑的比较快。
边栏推荐
- GBASE 8s的包
- 记录小知识点
- 冰冰学习笔记:循环队列的实现
- mongodb集群
- Anaconda installation +tensorflow installation +keras installation +numpy installation (including image and version information compatibility issues)
- 小白学习MySQL - 统计的'投机取巧'
- JS call() and apply()
- 重磅直播 | 相移法+多频外差之数学原理推导+实现
- CTF_ Web: deserialization of learning notes (II) CTF classic test questions from shallow to deep
- CTF_ Web: Advanced questions of attack and defense world expert zone WP (19-21)
猜你喜欢

jsz中的join()

大话云原生数据库中的存算分离

Musk released humanoid robot. Why is AI significant to musk?

CTF_ Web: deserialization of learning notes (II) CTF classic test questions from shallow to deep

Concat() in JS

2.0SpingMVC使用RESTful

Bingbing's learning notes: implementation of circular queue

Mongodb cluster

冰冰学习笔记:循环队列的实现

机器学习深度学习——向量化
随机推荐
CTF_ Web:8-bit controllable character getshell
Trigger for gbase 8s
记录小知识点
Vscode 设置clang-format
GBASE 8s的数据视图
Concat() in JS
Basic introduction of gbase 8s blocking technology
三角形类(构造与析构)
GBASE 8s 索引R树
Value transfer between parent and child components of wechat applet
GBASE 8s 总体架构
Php7.2 add JPEG extension
Record the problem of C # print size once
GBASE 8s存储过程执行和删除
GBASE 8s的包
php开发支付宝支付功能之扫码支付流程图
Cannot import name 'escape' from 'jinja2' [solved successfully]
哪个编程语言实现hello world最烦琐?
js的arguments
cnpm : 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本。