I2C控制器的Verilog建模之二
前言:接着上一篇的I2C写操作,今天要实现一个I2C的读操作。虽然在ADV7181B配置内部寄存器时没有必要使用到读操作,但是为了进一步确认寄存器是否在I2C写模块下被正确配置,这一步是必不可少的。
设计思路:由于最终的应用里I2C读模块在调试结束后还是要被剔除,因此决定还是另外建一个读的状态机独立于之前的写状态机。读状态机的思路基本和写状态机的思路一样,需要注意的是一次写操作需要两次的START信号和最后一字节传输结束后的NON-ACKNOWLEDGE。
改进和注意点:相比之前的写模块,读模块完善了以下这些
(a)时钟信号在一系列写操作完毕之后拉高,不再跳变;
(b)添加了使能信号,便于安排模块在写模块完成一些了写操作后才开始被激励工作;
(c)三态口:修改了SDAT_R,使之在LINK为0,即三态口读方向时候SDAR_T保持1。以及LINK信号在空闲状态下释放总线;
未解决的问题:因为读写两个独立的模块各自用到一个三态口,即各自有一对I2C_SCLK和I2C_SDAT,因此在顶层就驱动同一个芯片的引脚之前,还需要一个复用的逻辑开关分时切换这两个驱动源。因为之前没有这样用多个三态口驱动一个同一个三态口,综合是否可行将在下一篇I2C测试里给出答案。p.s.当然如果整合两个状态机到同一个模块下就不存在这样的问题。
源码:
- `timescale ns / ps
- `define SIM
- `define SYS_CLK
- `define I2C_CLK
- `define I2C_DIV `SYS_CLK/`I2C_CLK
- `define ADV7180
- `define SCLK_CNT_WIDTH
- //version:v1.0
- //mend bug: WHEN IN IDLE SET SCLK HIGH;
- module i2c_read_controller(
- sys_clk,
- sys_rst_n,
- sys_rreq_i,
- sys_rd_en,
- rd_reg_addr_i,
- sys_data_o,
- i2c_rd_idle_o,
- i2c_rd_ack_o,
- i2c_sclk,
- i2c_sdat
- );
- input sys_clk;
- input sys_rst_n;
- input sys_rreq_i;
- input sys_rd_en; //由其他信号激励使能
- input [:] rd_reg_addr_i; //从机寄存器地址
- output [:] sys_data_o; //待写的数据
- output i2c_rd_idle_o; //模块空闲
- output i2c_rd_ack_o; //非I2C的ACK,模块的ack
- output i2c_sclk;
- inout i2c_sdat;
- `ifdef ADV7180
- parameter DEVICE_READ = 'h40; //器件读操作地址
- parameter DEVICE_WRITE = 'h41; //器件写操作地址
- `endif
- `ifdef SIM
- parameter ST_WIDTH = ;
- parameter IDLE = "IDLE...",
- START1 = "START1.",
- WR_SLAVE = "WR_SLAV",
- ACK1 = "ACK1...",
- SET_REG = "SET_REG",
- ACK2 = "ACK2...",
- START2 = "START2",
- RD_SLAVE = "RD_SLAV",
- ACK3 = "ACK3...",
- DATA = "DATA...",
- NACK4 = "NACK4..",
- STOP = "STOP...";
- `else
- `define FSM
- parameter ST_WIDTH = ;
- parameter IDLE = `FSM'b0000_0000_0001,
- START1 = `FSM'b0000_0000_0010, //写操作一共有1个start,读操作一共2个start
- WR_SLAVE = `FSM'b0000_0000_0100,
- ACK1 = `FSM'b0000_0000_1000,
- SET_REG = `FSM'b0000_0001_0000,
- ACK2 = `FSM'b0000_0010_0000,
- START2 = `FSM'b0000_0100_0000,
- RD_SLAVE = `FSM'b0000_1000_0000,
- ACK3 = `FSM'b0001_0000_0000,
- DATA = `FSM'b0010_0000_0000,
- NACK4 = `FSM'b0100_0000_0000,
- STOP = `FSM'b1000_0000_0000;
- `endif
- //caputre the posedge of sys_rreq_i;
- reg sys_rreq_r0 = ;
- always @ (posedge sys_clk) begin
- if(sys_rst_n == 'b0) sys_rreq_r0 <= 0;
- else sys_rreq_r0 <= sys_rreq_i;
- end
- wire do_rreq = sys_rreq_i & ~sys_rreq_r0 & sys_rd_en;
- //generate the rd_start;
- reg rd_start = ;
- always @ (posedge sys_clk) begin
- if(sys_rst_n == 'b0) rd_start <= 0;
- else if(i2c_rd_ack_o == 'b1) rd_start <= 0;
- else if(do_rreq) rd_start <= ;
- else rd_start <= rd_start;
- end
- //GENERATE SCLK_R
- reg [`SCLK_CNT_WIDTH-:] sclk_cnt = ;
- always @ (posedge sys_clk) begin
- if('b0 == sys_rst_n) sclk_cnt <= 0;
- else if((sclk_cnt < `I2C_DIV-)&&(sys_rd_en == 'b1)&&(rd_start == 1'b1)) sclk_cnt <= sclk_cnt + 'd1;
- else sclk_cnt <= ;
- end
- `define SCLK_POS (sclk_cnt == `SCLK_CNT_WIDTH'd499)
- `define SCLK_HIGH (sclk_cnt == `SCLK_CNT_WIDTH'd124)
- `define SCLK_NEG (sclk_cnt == `SCLK_CNT_WIDTH'd249)
- `define SCLK_LOW (sclk_cnt == `SCLK_CNT_WIDTH'd374)
- wire i2c_sclk_w;
- assign i2c_sclk_w = ((sys_rd_en == 'b1)&&(sclk_cnt <= `SCLK_CNT_WIDTH'd249))?'b1:1'b0;
- //FSM
- reg [:] data2host = ;
- reg [:] sys_data_o = ;
- reg sdat_r = ;
- reg link = ; //控制三态口读写方向,默认为读方向0,写时为1
- reg [:] bit_cnt = 'd0;
- reg [ST_WIDTH-:] c_st = IDLE;
- reg [ST_WIDTH-:] n_st = IDLE;
- //FSM-1
- always @ (posedge sys_clk) begin
- if('b0 == sys_rst_n) c_st <= IDLE;
- else c_st <= n_st;
- end
- //fsm-2
- //实际的状态转移中ack[2:0]比物理等待的ack少四分之一
- always @ (*) begin
- n_st = IDLE;
- case(c_st)
- IDLE:begin
- n_st = (rd_start == 'b1)?START1:IDLE;end
- START1:begin
- n_st = (`SCLK_LOW)?WR_SLAVE:START1;end //sclk为高电平中心时转移
- WR_SLAVE:begin
- n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?ACK1:WR_SLAVE;end//数据在低电平是更新
- ACK1:begin
- n_st = (`SCLK_NEG)?SET_REG:ACK1;end//为保证下一步设置寄存器,提前1/4进入下一个状态
- SET_REG:begin
- n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?ACK2:SET_REG;end//数据在低电平是更新
- ACK2:begin
- n_st = (`SCLK_NEG)?START2:ACK2;end
- START2:begin
- n_st = (`SCLK_NEG)?RD_SLAVE:START2;end
- RD_SLAVE:begin
- n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?ACK3:RD_SLAVE;end
- //为保证下一步设置寄存器,提前1/4进入下一个状态
- ACK3:begin
- n_st = (`SCLK_NEG)?DATA:ACK3;end
- DATA:begin
- n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?NACK4:DATA;end
- NACK4:begin
- n_st = (`SCLK_NEG)?STOP:NACK4;end
- STOP:begin
- n_st = (`SCLK_NEG)?IDLE:STOP;end
- default:begin
- n_st = IDLE;end
- endcase
- end
- //FSM-3
- always @ (posedge sys_clk) begin
- if(sys_rst_n == 'b0) begin
- link <= 'd0; //释放总线
- data2host <= 'd0;
- bit_cnt <= 'd0;
- sdat_r <= 'd1;
- sys_data_o <= ;
- end
- else begin
- case(c_st)
- IDLE:begin
- link <= 'd0;
- data2host <= DEVICE_WRITE;
- bit_cnt <= 'd0;
- sdat_r <= 'd1;
- sys_data_o <= sys_data_o;
- end
- START1:begin
- link <= 'd1;
- sys_data_o <= sys_data_o;
- bit_cnt <= 'd1;
- data2host <= (`SCLK_LOW)?data2host<<:data2host;
- if(`SCLK_HIGH) begin
- sdat_r <= 'b0;end
- else if(`SCLK_LOW) begin
- sdat_r <= data2host[];end //pull down,由于data2host缓存一级的缘故,需要提前在START里输出第MSB位
- else begin
- sdat_r <= sdat_r;end
- end
- WR_SLAVE:begin
- sys_data_o <= sys_data_o;
- if(`SCLK_LOW) begin
- link <= (bit_cnt == 'd8)?1'b0:'b1; //释放数据总线
- bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
- data2host <= {data2host[:],'d0};//左移一位
- sdat_r <= (bit_cnt == 'd8)?1'd1:data2host[];end
- else begin
- link <= link;
- bit_cnt <= bit_cnt;
- data2host <= data2host;
- sdat_r <= sdat_r;end
- end
- ACK1:begin
- link <= 'd0;
- sys_data_o <= sys_data_o;
- data2host <= (`SCLK_POS)?rd_reg_addr_i:data2host; //读入待写的寄存器地址
- bit_cnt <= 'd0;
- sdat_r <= 'd1;
- end
- SET_REG:begin
- sys_data_o <= sys_data_o;
- if(`SCLK_LOW) begin
- link <= (bit_cnt == 'd8)?1'b0:'b1; //释放数据总线
- bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
- data2host <= {data2host[:],'d0};//左移一位
- sdat_r <= (bit_cnt == 'd8)?1'd1:data2host[];end
- else begin
- link <= link;
- bit_cnt <= bit_cnt;
- data2host <= data2host;
- sdat_r <= sdat_r;end
- end
- ACK2:begin
- link <= 'd0;
- sys_data_o <= sys_data_o;
- data2host <= (`SCLK_POS)?DEVICE_READ:data2host; //读入待写的寄存器地址
- bit_cnt <= 'd0;
- sdat_r <= 'd1;
- end
- START2:begin
- link <= (`SCLK_LOW)?'b1:link;
- sys_data_o <= sys_data_o;
- data2host <= data2host;
- bit_cnt <= bit_cnt;
- sdat_r <= (`SCLK_HIGH)?'b0:sdat_r;
- end
- RD_SLAVE:begin
- sys_data_o <= sys_data_o;
- if(`SCLK_LOW) begin
- link <= (bit_cnt == 'd8)?1'b0:'b1;
- bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
- data2host <= {data2host[:],'d0};//左移一位
- sdat_r <= (bit_cnt == 'd8)?1'd1:data2host[];end
- else begin
- link <= link;
- bit_cnt <= bit_cnt;
- data2host <= data2host;
- sdat_r <= sdat_r;end
- end
- ACK3:begin
- link <= 'b0;
- bit_cnt <= 'd0;
- sys_data_o <= sys_data_o;
- data2host <= ;
- sdat_r <= 'd1;end
- DATA:begin
- sys_data_o <= sys_data_o;
- if(`SCLK_HIGH) begin
- link <= (bit_cnt == 'd8)?1'b1:'b0; //为主设备产生NACK准备
- bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
- data2host[:] <= data2host[:];//左移一位
- data2host[] <= sdat_r;end
- else begin
- link <= link;
- bit_cnt <= bit_cnt;
- data2host <= data2host;
- sdat_r <= sdat_r;end
- end
- NACK4:begin
- link <= 'd1;
- sdat_r <= 'd1;//预先拉低
- bit_cnt <= bit_cnt;
- sys_data_o <= data2host;end
- STOP:begin
- link <= 'b1;
- bit_cnt <= bit_cnt;
- sys_data_o <= sys_data_o;
- data2host <= data2host;
- if(`SCLK_LOW) begin
- sdat_r <= 'b0;end
- else if(`SCLK_HIGH) begin
- sdat_r <= 'b1;end
- else begin
- sdat_r <= sdat_r;end
- end
- default:begin
- link <= 'd0;
- data2host <= 'd0;
- sys_data_o <= sys_data_o;
- bit_cnt <= 'd0;
- sdat_r <= 'd1;
- end
- endcase
- end
- end
- //assign
- assign i2c_sdat = (link == 'b1)?sdat_r:8'hzz;
- assign i2c_rd_idle_o = (c_st == IDLE)?'b1:1'b0;
- assign i2c_rd_ack_o = ((c_st == STOP)&&(`SCLK_NEG))?'b1:1'b0;
- assign i2c_sclk = (c_st != IDLE)?i2c_sclk_w:'b1;
- endmodule
控制读模块源码2:
- `timescale ns / ps
- `define LUT_WIDTH
- module adv7181_read_back(
- sys_clk,
- sys_rst_n,
- i2c_rd_ack_i,
- rd_back_done_o,
- sys_rreq_o,
- rd_reg_addr_o
- );
- input sys_clk;
- input sys_rst_n;
- input i2c_rd_ack_i;
- output rd_back_done_o;
- output sys_rreq_o;
- output [:] rd_reg_addr_o;
- //generate rreq_o
- reg sys_rreq_o = ;
- reg [`LUT_WIDTH-:] lut_index = ;
- reg [:] lut_data = ;
- always @ (posedge sys_clk) begin
- if('b0 == sys_rst_n) begin
- sys_rreq_o <= ;
- lut_index <= ;end
- else if((i2c_rd_ack_i == 'b1)&&(rd_back_done_o == 1'b0)) begin
- sys_rreq_o <= ;
- lut_index <= lut_index + 'd1;end
- else begin
- sys_rreq_o <= ;
- lut_index <= lut_index;end
- end
- //assign
- assign rd_back_done_o = (lut_index == `LUT_WIDTH'd15)?1'b1:'b0;
- assign rd_reg_addr_o = lut_data;
- //lut
- always @ (*) begin
- case(lut_index)
- `LUT_WIDTH'd0:lut_data <= 8'h23;
- `LUT_WIDTH'd1:lut_data <= 8'h41;
- `LUT_WIDTH'd2:lut_data <= 8'hf2;
- `LUT_WIDTH'd3:lut_data <= 8'ha3;
- `LUT_WIDTH'd4:lut_data <= 8'h43;
- `LUT_WIDTH'd5:lut_data <= 8'h13;
- `LUT_WIDTH'd6:lut_data <= 8'h65;
- `LUT_WIDTH'd7:lut_data <= 8'h76;
- `LUT_WIDTH'd8:lut_data <= 8'h85;
- `LUT_WIDTH'd9:lut_data <= 8'h93;
- `LUT_WIDTH'd10:lut_data <= 8'h14;
- `LUT_WIDTH'd11:lut_data <= 8'h13;
- `LUT_WIDTH'd12:lut_data <= 8'h15;
- `LUT_WIDTH'd13:lut_data <= 8'h11;
- `LUT_WIDTH'd14:lut_data <= 8'h11;
- `LUT_WIDTH'd15:lut_data <= 8'h19;
- endcase
- end
- endmodule
仿真源码3:
- `timescale ns / ps
- module tb_read();
- reg sys_clk;
- reg sys_rst_n;
- initial begin
- sys_clk=;
- sys_rst_n=;
- # sys_rst_n=;
- end
- always begin
- # sys_clk=~sys_clk;end
- wire i2c_rd_ack;
- wire sys_rreq;
- wire [:] rd_reg_addr;
- wire i2c_sclk;
- wire i2c_sdat;
- wire i2c_rd_idle;
- wire [:] sys_data_out;
- i2c_read_controller u0(
- .sys_clk( sys_clk ),
- .sys_rst_n( sys_rst_n ),
- .sys_rreq_i( sys_rreq ),
- .sys_rd_en( 'b1 ),
- .rd_reg_addr_i( rd_reg_addr ),
- .sys_data_o( sys_data_out ),
- .i2c_rd_idle_o( i2c_rd_idle ),
- .i2c_rd_ack_o( i2c_rd_ack ),
- .i2c_sclk( i2c_sclk ),
- .i2c_sdat( i2c_sdat )
- );
- wire rd_back_done;
- adv7181_read_back u1(
- .sys_clk( sys_clk ),
- .sys_rst_n( sys_rst_n ),
- .i2c_rd_ack_i( i2c_rd_ack ),
- .rd_back_done_o( rd_back_done ),
- .sys_rreq_o( sys_rreq ),
- .rd_reg_addr_o( rd_reg_addr )
- );
- endmodule
I2C控制器的Verilog建模之二的更多相关文章
- Norflash控制器的Verilog建模之二(仿真)
前言:经过几天修改,norflash控制器基本已经完成,通过仿真.完整的norflash包含2个模块:直接操作硬件的norflash_ctrl.v与控制ctrl模块的驱动norflash_driver ...
- I2C控制器的Verilog建模之一
前言:之前申请了ADI公司的一款ADV7181CBSTZ的视频解码芯片,正好原装DE2板子安的是同系列的ADV7181BBSTZ.虽然都是ADV7181的宗出,但是寄存器配置等等还是有些诧异,引脚也不 ...
- I2C控制器的Verilog建模之三(完结版)
前言:终于到了测试篇,不过悲剧了一下.按照之前<二>里面的思路,在顶层用一个复用器驱动读写独立模块的I2C总线确实失败.虽然综合过去了,不过警告里已经说明:底层的2个原本是inout三态口 ...
- Norflash控制器的Verilog建模之一
摘要:今天驱动一款SPANSION公司生产的norflash——S29AL032D70,没有别的参考资料,大致了解一下norflash的内部cmos电路架构以及其用途之后,直接看手册吧. 如何看手册: ...
- SDRAM控制器的Verilog建模之一
前言:作为经典存储器的三剑客中的flash和sram已经建模测试过了,虽然现在都已经ddr2,ddr3,667MHZ.1333MHZ的天下了,但是接下这周来准备写一下sdram的controller. ...
- 异步SRAM控制器的Verilog建模
前言:sram顾名思义静态随机存储器,分为asram异步型和ssram同步型.这里驱动DE2上一块ISSI公司的512KB的asram. 设计思路:因为实际应用中单字节读写效率不高,所以本设计中仿照s ...
- Norflash控制器的Verilog建模之三(測試)
前言:回校了,辦好手續就著手寫測試篇.初步的norflash控制器已經完成,通過硬件測試.目前的norflash完成扇区块擦除.单字节写.单字节读3个功能.博文最后附上源码. 总结:和之前的博文一样, ...
- VGA逐行扫描控制器的Verilog建模
前言:因为VGA是一种模拟图像传输数据接口,所要将数字信号用DAC转换成模拟量.本文用的一款ADI公司高精度的视频IC,实则一款高带宽的视频DAC.因为VGA时序较为简单,并且网上的VGA驱动基本大同 ...
- Linux i2c子系统(四) _从i2c-s3c24xx.c看i2c控制器驱动的编写
"./drivers/i2c/busses/i2c-s3c2410.c"是3.14.0内核中三星SoC的i2c控制器驱动程序, 本文试图通过对这个程序的分析, 剥离繁复的细节, 总 ...
随机推荐
- Hibernate 映射关系
映射组成关系 •建立域模型和关系数据模型有着不同的出发点: –域模型: 由程序代码组成, 通过细化持久化类的的粒度可提高代码的可重用性, 简化编程 –在没有数据冗余的情况下, 应该尽可能减少表的数目, ...
- CentOS6.4安装Hadoop2.0.5 alpha - Single Node Cluster
1.安装JDK7 rpm到/usr/java/jdk1.7.0_40,并建立软链接/usr/java/default到/usr/java/jdk1.7.0_40 [root@server-308 ~] ...
- MJRefresh的一个注意事项
如果从视图一跳转到视图二之后,在视图二中进行MJRefresh的刷新操作,那么在推出试图二之前要用dealloc函数将MJRefreshHeaderView或者MJRefreshFooterView释 ...
- MyJni撒旦
package com.baidu.jnitest; import android.os.Bundle; import android.app.Activity; import android.vie ...
- Android_Layout (一)
layout (布局) --->Android 有五大布局,分别是: LinearLayout : 线性布局,子组件按照垂直或者水平方向来布局. RelativeLayout :相对布局,按照 ...
- QT下调用摄像头(opencv2.4.4)
http://www.cnblogs.com/yuliyang/p/3525107.html 项目pro文件: #------------------------------------------- ...
- Using python to process Big Data
Pandas is a great lib to process BIg Data. 1) pandas.pivot_table(data,values=None,columns=None,aggfu ...
- kinnect相关
1. kinnect的现状. http://tech.qq.com/a/20150909/046760.htm 2. kinnect的相关工作 http://baike.baidu.com/link? ...
- js创建对象的6种方式
一.工厂模式 function createStudent(name,age){ var o=new Object(); o.name=name; o.age=age; o.myName=functi ...
- Map/Reduce 工作机制分析 --- 数据的流向分析
前言 在MapReduce程序中,待处理的数据最开始是放在HDFS上的,这点无异议. 接下来,数据被会被送往一个个Map节点中去,这也无异议. 下面问题来了:数据在被Map节点处理完后,再何去何从呢? ...