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控制器驱动程序, 本文试图通过对这个程序的分析, 剥离繁复的细节, 总 ...
随机推荐
- 一步步调试解决iOS内存泄漏
虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在.所以了解原理很重要. 这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄 ...
- c语言 四种方法调用数组
#include <stdio.h> /********************************* * 方法1: 第一维的长度可以不指定 * * 但必须指定第二维的长度 * * ...
- 利用Jmeter进行Web测试
JMeter介绍 脚本录制 运行JMeter进行测试 JMeter主要组件介绍 参数化设置 动态数据关联 使用命令行运行JMeter脚本 利用XSLT分析JMeter结果文件 1:JMeter,一个1 ...
- 学会使用Constant常量或者Enum枚举
好多时候我们在数据库表中存放的类型是一个代号CHAR(1) 0,1,2,3等分别代表些什么. 那么你是怎么知道0,1,2,3代表什么的呢? 有的是建表,连接查询,但很少人用. 有的是在jsp页面c:i ...
- Activity之间数据传递(一)(简单传递,数据包Bundle,传递对象)
一,简单传递(简单的字符串) 第一个activity通过putExtra()将字符串传入i protected void onCreate(Bundle savedInstanceState) { s ...
- window2008 r2 负载均衡
两台服务器win2008 r2 ,iis7.5 ip地址192.168.5.16, 192.168.5.18 虚拟ip 192.168.5.30 设置过程: 1.在两台服务器上安装负载均衡模块 ...
- Java网络应用编程
1,网络连接 (1)用户向服务器发送请求(Socket); (2)服务器向用户发送信息(ServerSocket),一直监听的话用.accept(); 2,信息发送与接收 (1)客户向服务器端发送信息 ...
- GCD线程间通信
从子线程回到主线程 dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执 ...
- 一些常用的vim设置
以下内容皆来源于网络,感谢原作者.如果引用出处错误,请告知以便修改. 1. vim的几种模式和按键映射 转载自:[1] Map是Vim强大的一个重要原因,可以自定义各种快捷键,用起来自然得心应手.vi ...
- git error
一,今天在上传代码时出错: $ git push -u origin mastererror: The requested URL returned error: 403 Forbidden whil ...