距离上次更新专栏都两个月过去了,知乎的推荐算法还是比较良心,我这样断更法,每天被阅读数都还蛮不错的,提出表扬233
断更倒不是因为我懒,而是因为被老师召唤回了学校,还被多塞进了一个组。这段时间,投简历,期末考试,两个组的活,机考复习等等事务全扑上来,酸爽~~(我还作死报了个班学唱歌233) 不过也好,忙着总比闲着好,学习速度比在家快多了。
嘛,在总结今天的内容之前,先挖几个坑,等我定下工作,有下面这么些东西可以慢慢更:
1.紫光、Xilinx、Altera跑同样代码的效果比较 。
(这三家板子我现在是已经跑成了一些工程的。另外我甚至还想挤点时间,把DC的综合效果也一并加入比较,反正学校服务器不用白不用)
2.跨时钟域专题。
3.时序约束专题。
4.IEEE浮点规则、浮点定点转换verilog代码。
5.一些数电面试常考内容。比如异步FIFO、格雷码、三种RAM与不同读写模式的写法、三种复位方式、消抖、序列发生、序列检测这些。
都是蛮简单的东西,到时候在github仓库上整理好后,简单点一下,然后分享链接出来就好了。
6.CUDA编程,这是目前为另一个组的项目刚开始学的东西,坑先挖着,一口也吃不成胖子,慢慢学慢慢输出呗。
话说了解了CUDA之后我开始为FPGA的未来担心起来233 CUDA感觉上也太强大了。
终于到今天的内容了,DPI (Direct Programming Interface),systemverilog用于与C沟通的桥梁,久闻大名了,一直没有操弄过。今天看到”数字芯片实验室“的公众号更新了这个内容,虽然面试题还有一大堆都没看完,还是决定来把玩一番。很有意思!公众号的文章链接我放这了。基于QuestaSIM的SystemVerilog DPI使用流程(step by step)
我这个文章重点要讲的是玩完他的代码后,根据《systemverilog测试平台指南》这本书的讲解改了两段代码来跑的效果。
第一个是一段特简单的乘方代码,重点只是测一下sv能不能通过加automatic关键字的方法,自动给吃进去的c代码做迭代。
第二个是试了一下如果c代码里有指针和动态分配的内容,sv该如何处理。
乘方代码
(1)c代码
#include "stdio.h"
#include "dpi_types.h"
int factorial(int i)
{
printf("factorial n");
if (i == 1)
return 1;
else
return i*factorial(i - 1);
}
这里要注意我们添加的dpi_types.h,光在c里面这么写还是不够的,还需要在questasim的vlog指令里让questasim去读入这个dpi_types.h。
(2)bat中的questasim指令
vlib work
vlog test.sv -dpiheader dpi_types.h dpi_test.c
vopt +acc test -o opt_test
vsim -i opt_test -do " view source
(3)乘方的sv代码
module test ();
import "DPI-C" function int factorial(input int i);
test1 t1();
endmodule
program automatic test1();
initial begin
for(int i =1;i <=10;i=i+1)
$display("%0d! = %0d",i ,factorial(i));
end
endprogram
跑的效果:
计数器代码
具体做的事情是:在C代码里动态分配一块内存,做个计数器。
(1)c代码:
#include "stdio.h"
#include "veriuser.h"
#include "svdpi.h"
#include "malloc.h"
typedef struct{
unsigned char cnt;
}cnt_mem;
void* counter8_allocate_mem(){
cnt_mem* c = (cnt_mem*) malloc(sizeof(cnt_mem));
c->cnt = 0;
printf("malloc a %d byte memory to instn",sizeof(cnt_mem));
return c;
}
void counter8(
cnt_mem* inst_c,
svBitVecVal* dout_c,
svBitVecVal* din_c,
svBit reset_c,
svBit load_c
)
{
if(reset_c)
inst_c->cnt = 0;
else if (load_c)
inst_c->cnt = *din_c;
else
inst_c->cnt++;
*dout_c = inst_c->cnt;
printf("count = %d, i = %d, reset = %d, load = %dn",*dout_c,*din_c,reset_c,load_c);
}
这里注意:svBit、svBitVecVal* 这些类型,第一次用的人肯定是陌生的,不过这其实就是几个在c中的bit,指针概念在sv中的等效表达而已。网上一搜那种DPI变量类型的对照表格就明白了。
(2)bat文件,跟上一个工程完全一样。
(3)sv代码:
具体做的事情是:
1.通过import"DPI-C"这句话把c代码里的两个函数(一个用于分内存、一个用于做计数)吃进来。这里要注意,我刻意让第二句“DPI-C”的参数名后缀写成跟所有地方都不一样的_a,用来表示这个地方的命名不重要。
2.然后把第一个函数动态分配的内存,通过chandle传进第二个计数器函数进行计数。计数器函数的调用,其实就相当于我们正常情况下在testbench里面例化module,这里就要注意连线了。这里连出去到testbench层的线是可以在questasim里面画时序图的。
3.写好testbench连上计数器函数(或者叫她module)的连线的初始化,时钟,控制信号变化。跑起来后,testbench在每个时钟上升沿调用计数器的c代码,就相当于调用一个必然在一个周期内可以跑完的组合逻辑。
计数器函数每次被调用后,将结果通过连线返回给testbench。
module test ();
import "DPI-C" function chandle counter8_allocate_mem();
import "DPI-C" function void counter8(
input chandle inst_a,
output bit[7:0] dout_a,
input bit[7:0] din_a,
input bit reset_a,load_a
);
bit[7:0] dout_w,din_w;
bit reset_w,load_w;
chandle inst1;
bit clk;
initial begin
inst1 = counter8_allocate_mem();//动态分配内存
reset_w = 0;
load_w = 0;
din_w = 8'd233;
fork
forever #10 clk = ~clk;
forever @(posedge clk) begin
counter8(inst1,dout_w,din_w,reset_w,load_w);//把内存传进计数器函数
end
join_none
#10
load_w =1;
#20
load_w =0;
#1000
$finish;
end
endmodule
跑的效果:(我这里是给的初值就是233)