很久没更新了,这边文章是笔者在隔离期间写的,之前接触过inout类型的仿真,但很久未使用有些生疏了,查阅了相关资料编写了这篇文章,一来是当作笔记,忘记时随时查阅,二来是为了供广大FPGA爱好者学习参考,如有纰漏,请批评指正。
我们都知道,在电路中有输入端口(input)、输出端口(output)、双向口(inout)。像诸如I2C协议的器件都是采用两根线进行主从通讯,一个是时钟线sck,另一个是数据线sda。在主机与从机需要通讯时,主机就会往从机发生sck信号,若主机需要往从机发生数据,sda信号则从主机流向从机(如1流向),反之,主机需要得到从机的数据,sda信号则从从机流向主机(如2流向),I2C协议还有很多知识点,本文只对双向口仿真进行阐述。此时可以发现sda信号在读写两个状态时流向不同,那这种信号该如何仿真呢?
在写Verilog程序时,我们都知道reg类型只能在initial、always等里面赋值,而wire类型可以在assign里面赋值。作为双向口的sda信号我们既希望它作为输入,也希望它作为输出,因此wire类型最为合适。
对于双向口的仿真,我通常会在tb文件中这样定义:
reg sck;
wire sda;
reg sda_r;
reg sda_en;
assign sda_r = (sda_en) ? mosi : 1'bz;
assign sda =sda_r;
当sda作为输入时,sda_en应为0,此时sda为高阻态,sda会相应外界连接的信号高低电平。当作为输出时,sda_en应为1,此时mosi作为要输出的sda的数据,根据时序对应写出仿真文件即可。
由于i2c的时序都是固定的,为了方便仿真文件的编写,我会采用task语句将时序打包,方便自己对程序的理解。
task task_name;
input inputname1;
input inputname2;
..
reg regname1;
reg regname2;
..
begin
... //具体程序执行的内容
...
end
endtask
在initial里面使用task_name(inputname1,inputname2,…);即可实现功能。
上图为我镶嵌的iicwp的测试代码,其中iic_m_write也是有task语句编写的函数,该功能会向iic中发送8’h04,8’h00,8’h00,8’h01,8’h01。
仿真截图如下:
今天的分享就到这里,如有错误,请指正,谢谢。
欢迎关注我的个人公众号,有问题可留言。