//////////////////////////////////////////////////
//clk = 20 MHz ,一个周期50ns
//sck = 100 kHz (scl) ,一个周期 1000ns
//I2C在sck下降沿更新数据,上升沿读取(采样)数据
///////////////////////////////////////////////////
module demo_I2C #(parameter F100K = 'd200)(clk,rstn,start_sig,word_addr,wr_data,rd_data,done_sig,scl,sda,sq_i); input clk ;
input rstn ; input [:] start_sig ; //
input [:] word_addr ; //word address
input [:] wr_data ; //Data
output [:] rd_data ; //Data from EEPROM
output done_sig ; output scl ; //sda和scl其实是用来作为仿真信号添加在这里的,寄存器信号都用rscl和rsda表示了,最后用assign将rscl和rsda赋值给sda和scl,连到模块外部仿真用
inout sda ; //sda表示当前sda的in或out的值 output [:] sq_i ;
/************************************
在这里,iic_func_module.v 的步骤i已经被引出来了。读者要知道步骤i在无论是在设计上还是仿真上都有许多的好处。
步骤i在仿真中可以充当“调试跟踪”的作用,因为只要模块的那个部分出问题,步骤i就会指向它。
此外,步骤i在驱动IO口的时候,我们还可以知道仿真对象的内部到底发生什么事情了。
*************************************/ reg [:] i ;
reg [:] cnt ;
reg [:] go ;
reg isout ;
reg isack ; //临时存放ack信号用于判断
reg [:] rdata ; //存放任意8位数据的寄存器。在读的最后一步,还会将读到的8位sda存起来赋值给rd_data
reg rsda ; //用来寄存任意一位sda
reg rscl ;
reg rdone_sig ; always@(posedge clk or negedge rstn)
begin
if(!rstn)
begin
// start_sig <= 2'b00 ; /*输入信号不是寄存器类型,不需要Reset*/
// word_addr <= 8'd0 ; /*在处理输入输出信号时,输入信号因为不是reg而是wire,不需要Reset*/
// wr_data <= 8'd0 ; /*输出信号一般也不直接Reset,而是定义一个他们对应的reg,在Reset或者其他操作时对这些reg进行操作,最后用assign将输出信号和各自的reg相连*/
rdata <= 'd0 ;
rdone_sig <= 'b0 ; rscl <= 'b1 ;
rsda <= 'b1 ;
i <= 'd0 ;
isout <= 'b1 ;
isack <= 'b0 ;
rdata <= 'd0 ;
go <= 'd0 ;
end else if(start_sig[]) //write option
case(i)
: //start
begin
if(cnt == 'd0)
begin
rscl <= 'b1 ;
rsda <= 'b1 ;
end
else cnt <= cnt + 'b1; if(cnt == 'd100)
begin
rsda <= 'b0 ; end
else cnt <= cnt + 'b1; if(cnt == F100K - 'b1)
begin
i <= i + 'd1 ;
cnt <= ;
end
else cnt <= cnt + 'b1;
end : //write device address
begin
isout = 'b1;
rdata <= {'b1010,3'b000,'b0}; //1010是EEPROM型号,000是这颗EEPROM地址(三个引脚全部接地),0表示/W(写)
i <= 'd7;
go <= i + 'b1;
end : //write word address
begin
isout = 'b1;
i <= 'd7;
rdata <= word_addr;
go <= i + 'b1;
end : //write data
begin
isout = 'b1;
i <= 'd7;
rdata <= wr_data;
go <= i + 'b1;
end : //stop
begin
if(cnt == 'd0)
rscl <= 'b0 ;
else if(cnt == 'd50)
rscl <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == 'd0)
rsda <= 'b0 ;
else if(cnt == 'd150)
rsda <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == 'd50 + F100K - 1'b1)
begin
i <= i + 'b1 ;
cnt <= 'd0 ;
end
else
cnt <= cnt + 'b1 ;
end : //return done_sig
begin
rdone_sig <= 'b1;
i <= i + 'b1;
end
: //return IDLE
begin
rdone_sig <= 'b0;
i <= 'd0;
end
,,,,,,,:
begin
isout = 'b1;
rsda <= rdata[ - i];
if(cnt == 'd0)
rscl <= 'b0 ;
else if(cnt == 'd100)
rscl <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == F100K - )
begin
i <= i + 'b1 ;
cnt <= 'b0 ;
end
else
cnt <= cnt + 'b1 ; end
: //waiting for acknowledge
begin
isout = 'b0; //等待应答时是Read,因此是输入模式,=表示即时响应
if(cnt == 'b0)
rscl <= 'b0;
else if(cnt == 'b100)
rscl <= 'b1;
else
cnt <= cnt + 'b1; if(cnt == F100K - )
begin
i <= i + 'b1;
cnt <= ;
end
else
cnt <= cnt + 'b1; if(cnt == 'd150)
isack <= sda; //保险起见,在150个clk后才进行ack读取
else
cnt <= cnt + 'b1;
end
: //判断是否应答,返回go
begin
if(!isack)
i <= go;
else
i <= ;
end default: i <= ;
endcase
/***************************************************************************************************************************************/
else if(start_sig[]) //read option
case(i) //读写操作不冲突,i 不冲突
: //start
begin
if(cnt == )
begin
rscl <= 'b1 ;
rsda <= 'b1 ;
end
else cnt <= cnt + 'b1; if(cnt == )
begin
rsda <= 'b0 ; end
else cnt <= cnt + 'b1; if(cnt == F100K - )
begin
i <= i + 'd1 ;
cnt <= ;
end
else cnt <= cnt + 'b1;
end
: //write device address (read前先要write获得从机应答)
begin
isout = 'b1;
rdata <= {'b1010,3'b000,'b0}; //1010是EEPROM型号,000是这颗EEPROM地址(三个引脚全部接地),0表示/W(写)
i <= 'd10;
go <= i + 'b1;
end
: //write word address
begin
isout = 'b1;
rdata <= word_addr;
i <= 'd10;
go <= i + 'b1;
end
: //start again ,需要再控制sda和scl共同作用产生start信号
begin
isout = 'b1;
if(cnt == )
begin
rscl <= 'b0 ;
rsda <= 'b0 ;
end
else cnt <= cnt + 'b1; if(cnt == )
begin
rsda <= 'b1 ;
rscl <= 'b1 ;
end
else cnt <= cnt + 'b1; if(cnt == )
begin
rsda <= 'b0 ; end
else cnt <= cnt + 'b1;
if(cnt == )
begin
rscl <= 'b0 ; //这时EEPROM已经start了 end
else cnt <= cnt + 'b1;
if(cnt == - ) //保险起见,等到start稳定再进入下一状态
begin
i <= i + 'd1 ;
cnt <= ;
end
else cnt <= cnt + 'b1;
end : // 再写一次device address,告诉从设备变成read了
begin
isout = 'b1; //切换到输入(读取)模式
rdata <= {'b1010,3'b000,'b1}; //1010是EEPROM型号,000是这颗EEPROM地址(三个引脚全部接地),1表示R(读)
i <= 'd10;
go <= i + 'b1;
end
: //read data
begin
isout = 'b0;
rdata <= 'd0; ///* 注意这里,在读取8位sda寄存在rdata之前,要先将rdata清零*///
i <= 'd20;
go <= i + 'b1;
end : //stop
begin
isout = 'b1; if(cnt == )
rscl <= 'b0 ;
else if(cnt == )
rscl <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == )
rsda <= 'b0 ;
else if(cnt == )
rsda <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == + F100K - )
begin
i <= i + 'b1 ;
cnt <= ;
end
else
cnt <= cnt + 'b1 ;
end : //return isdone
begin
rdone_sig <= 'b1;
i <= i + 'b1;
end
: //return IDLE
begin
rdone_sig <= 'b0;
i <= ;
end
,,,,,,,:
begin
isout = 'b1;
rsda <= rdata[ - i];
if(cnt == )
rscl <= 'b0 ;
else if(cnt == )
rscl <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == F100K - )
begin
i <= i + 'b1;
cnt <= ;
end
else
cnt <= cnt + 'b1 ; end
: //waiting for acknowledge
begin
isout = 'b0; //等待应答时是Read,因此是输入模式,=表示即时响应
if(cnt == )
rscl <= 'b0;
else if(cnt == )
rscl <= 'b1;
else
cnt <= cnt + 'b1; if(cnt == F100K - )
begin
i <= i + 'b1;
cnt <= ;
end
else
cnt <= cnt + 'b1; if(cnt == )
isack <= sda; //保险起见,在150个clk后才进行sda读取
else
cnt <= cnt + 'b1;
end
: //判断是否应答,返回go
begin
if(!isack)
i <= go;
else
i <= ;
end ,,,,,,,:
begin
isout = 'b0; if(cnt == )
rscl <= 'b0 ;
else if(cnt == )
rscl <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == F100K - )
begin
i <= i + 'b1 ;
cnt <= ;
end
else
cnt <= cnt + 'b1 ; if(cnt == ) //保险起见,在150个clk后才进行sda读取
rdata[- i] <= sda; //读写都是先对MSB操作
else
cnt <= cnt + 'b1; end : //no ack(由于是单Data读,不是连续Data读,所以不需要应答),但是scl还是要继续跳转的
begin
isout = 'b1; if(cnt == )
rscl <= 'b0 ;
else if(cnt == )
rscl <= 'b1 ;
else
cnt <= cnt + 'b1 ; if(cnt == F100K - )
begin
i <= go ;
cnt <= ;
end
else
cnt <= cnt + 'b1 ;
end default: i <= ;
endcase
else
begin
// start_sig <= 2'b00 ;
// addr_sig <= 8'd0 ;
// wr_data <= 8'd0 ;
rdata <= 'd0 ;
rdone_sig <= 'b0 ; rscl <= 'b1 ;
rsda <= 'b1 ;
i <= 'd0 ;
isout <= 'b1 ;
isack <= 'b0 ;
rdata <= 'd0 ;
go <= 'd0 ;
end
end assign sda = isout?rsda:'bz ; //sda是一个带三态门的inout端口,三态门在out线上,所以在输出情况下,要将isout打开,rsda传到sda上。在输入情况下,isout关闭,输出方
assign scl = rscl ; //向线是高阻态,但输入方向没有三态门,是导通状态,可以read或者ackownledge数据。
assign done_sig = rdone_sig ;
assign rd_data = rdata ; //rdata在读的最后一步,将8位sda都存了下来,所以要将rdata给rd_data assign sq_i = i ;
endmodule

--------------------------------------------------------------------------------------------------------------------------分割线----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 `timescale  ns/  ns
module demo_I2C_vlg_tst(); /*与其说这是一个仿真文件,倒不如说这是一个top文件,因为里面不仅给出了激励,还模拟出了EEPROM的应答信号,
与接口模块通过sq_i,done_sig,start_sig,inout口sda实现互相控制*/
/*这也就说明了为什么设计和验证不分家的原因,当设计模块很庞大的时候,只能通过FPGA上板进行原型验证,大到一定规模之后,
FPGA也无法满足,只能通过搭建一个验证平台模拟使用环境(可能是硬件类似这种EEPROMinout接口)来验证功能,testbench并不简单,更像top*/ reg clk;
reg rstn; reg [:] start_sig;
reg [:] word_addr;
reg [:] wr_data; // wires
wire done_sig;
wire [:] rd_data;
wire scl; //IO inout端口在写testbench时,输入reg和输出wire都要写
reg treg_sda; //输入
wire sda; //输出
assign sda = treg_sda; //由于是inout端口,要将输入输出连起来 wire [:] sq_i; demo_I2C i1 (
// port map - connection between master ports and signals/registers
.clk(clk),
.done_sig(done_sig),
.rd_data(rd_data),
.rstn(rstn),
.scl(scl),
.sda(sda),
.sq_i(sq_i),
.start_sig(start_sig),
.word_addr(word_addr),
.wr_data(wr_data)
);
initial
begin
//学习这里的Reset和clk写法
rstn = ;
# rstn = ;
clk = ;
forever # clk = ~clk; $display("Running testbench");
end reg [:] i; always@(posedge clk or negedge rstn) /*这里是对输入进行激励*/
if(!rstn)
begin
i <= 'd0;
start_sig <= 'd0;
word_addr <= 'd0;
wr_data <= 'd0;
end
else
case(i)
:
begin
if(done_sig) //第二步:写完成后,done_sig有一拍变1的动作,在这一拍跳到第三步
begin
start_sig <= 'd0;
i <= i + 'b1;
end
else //第一步:done_sig=0,先写
begin
start_sig <= 'b01;
word_addr <= 'b10101010;
wr_data <= 'b11110000;
end
end
:
begin
if(done_sig) //第四步:读操作完成后,done_sig会有一拍短暂的变1动作,在这一拍跳到第五步
begin
start_sig <= 'd0;
i <= i + 'b1;
end
else //第三步:在上一拍结束后,done_sig立刻变0,进行这一步读操作
begin
start_sig <= 'b10;
word_addr <= 'b10101010;
end
end
: //停止动作 //第五步:在第四步短暂的变1后立马回到0,并停留在这一步,表示这个写+读操作结束
i <= i;
default: i <= i;
endcase ///////////////////////////////////////////////
/*这一部分是对IO口的激励,模拟EEPROM的acknowledge操作,因为接口代码并不是上板和EEPROM通信,为了仿真接口代码的正确性
这里写出了在接口代码进行一段操作后EEPROM的应答信号,还有读取操作时EEPROM输入给接口的rd_data*/ always@(posedge clk or negedge rstn)
if(!rstn)
treg_sda = 'b1; //reset并不是输入状态(在根本上,如果仿真对象不是将IO设置为输入状态,无论怎样驱动和刺激都没有用)
else if(start_sig[])
case(sq_i)
:
treg_sda = 'b0; //在15状态时,isout拉低输出关断,输入导通,sda输入为0,即ack default: treg_sda = 'b1; //其他状态下,并不是输入状态(在根本上,如果仿真对象不是将IO设置为输入状态,无论怎样驱动和刺激都没有用)
endcase
else if(start_sig[])
case(sq_i)
:
treg_sda = 'b0; ,,,,,,,:
treg_sda = wr_data[ - sq_i]; //这些状态是read,isout拉低,是输入状态,进行8位数据传入 default: treg_sda = 'b1; //其他状态下,并不是输入状态(在根本上,如果仿真对象不是将IO设置为输入状态,无论怎样驱动和刺激都没有用)
endcase
else
treg_sda = 'b1; endmodule

学习笔记一:I2C协议学习和Verilog实现的更多相关文章

  1. [原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  3. 【神经网络与深度学习】学习笔记:AlexNet&Imagenet学习笔记

    学习笔记:AlexNet&Imagenet学习笔记 ImageNet(http://www.image-net.org)是李菲菲组的图像库,和WordNet 可以结合使用 (毕业于Caltec ...

  4. Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明-Babel)

    五  Vue学习-vue-cli脚手架学习(创建只选一个选项:Babel) 1. 项目目录说明 node_modules : 包管理文件夹 public : 静态资源 src : 源代码 gitign ...

  5. Vue学习笔记-Vue.js-2.X 学习(五)===>脚手架Vue-CLI(PyCharm)

    Vue项目在pycharm中配置 退出运行: ctrl+c Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明)

  6. Vue学习笔记-Vue.js-2.X 学习(四)===>脚手架Vue-CLI(基本工作和创建)

    (五) 脚手架Vue-CLI 一 Vue-CLI前提(nodejs和webpack) 二  Vue学习-nodejs按装配置,Node.js 就是运行在服务端的 JavaScript. 1. 去nod ...

  7. Vue学习笔记-Vue.js-2.X 学习(三)===>组件化高级

    (四) 组件化高级 1.插槽(slot)的基本使用 A:基本使用: <slot></slot> B:默认置:<slot><h1>中间可以放默认值< ...

  8. Vue学习笔记-Vue.js-2.X 学习(二)===>组件化开发

    ===重点重点开始 ========================== (三) 组件化开发 1.创建组件构造器: Vue.extends() 2.注册组件: Vue.component() 3.使用 ...

  9. Vue学习笔记-Vue.js-2.X 学习(一)===>基本知识学习

    一  使用环境: windows 7 64位操作系统 二  IDE:VSCode/PyCharm 三  Vue.js官网: https://cn.vuejs.org/ 四  下载安装引用 方式1:直接 ...

  10. I2C协议学习笔记

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/wzt_007/article/detai ...

随机推荐

  1. Eclipse 配置 maven 的两个 settings 文件

    eclipse配置的settings文件名完全可以自定义,而本机maven只认识settings.xml文件. eclipse里配置maven有一个叫全局的,有一个叫用户的.这两个文件可以和本机mav ...

  2. ORACLE数据库AWR工具学习

    AWR(Automatic Workload Repository)即工作负载信息库,主要用于收集数据库运行状态的资料库,通过AWR报告可以分析ORACLE数据库的性能,从而给出优化策略. 图一:此图 ...

  3. JSP九大内置对象与Servlet的对应关系

    JSP对象                              Servlet中怎样获得 request service方法中的request参数 response service方法中的res ...

  4. Linux 挂载

    千万不要挂载到 根目录下 也不要用 umount -fl  会死的 fdisk -l 看 能挂载的是哪个盘 格式化 mkfs.ext4 /dev/vde 创建一个文件 mkdir /testmnt 卸 ...

  5. .net Cookie的操作

    using System; using System.Collections.Generic; using System.Web; namespace Zhong.Core { /// <sum ...

  6. [翻译] TransitionKit

    TransitionKit https://github.com/blakewatters/TransitionKit A simple, elegantly designed block based ...

  7. November 20th 2016 Week 47th Sunday

    Learn from yesterday, live for today, look to tomorrow. 学习昨天,活在今天,展望明天. There is always room at the ...

  8. ZT 人生真的是一场马拉松吗?

    中国合伙人:孟晓俊:生活应该是什么样的?自己提出的问题应该由自己来回答,别人的回答是别人的答案,是别人的生活,而你应该过自己的生活,不是别人的生活.     人生真的是一场马拉松吗? 投递人 itwr ...

  9. fun()可拆分赋值 fun()可以拆, 变成 fun 和 括号, fun 可以赋值

    2. 函数名可以赋值给其他变量   --->   就是 func()可以拆 def fun (): print("哈哈") a = fun # 拆分 fun()的    fu ...

  10. 常见dos命令(win7下测试)

    按下组合键:win + R ,输入cmd进入Dos. 1.  cls :清屏命令. 2.  ver :查看系统版本号命令,winver弹出一个窗口显示更详细的系统版本号. 3.  dir  无参数 : ...