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
上一篇:webwervice发布时出错 java.security.PrivilegedActionException


下一篇:iOS - 网络 - NSURLSession