sccb不需要判断应答位
/*qin*/
module sccb_io
(
input clk,
input rst_n,
input sccb_write_req,
input sccb_read_req,
output sccb_ack,//数据发送完成
output [7:0] sccb_read_data,
input [6:0] lut_id,
input [15:0] lut_addr,
input [7:0] lut_data,
inout sccb_sda,
output reg sccb_scl
);
/*
sccb_io u_sccb_io(
.clk (clk ),
.rst_n (rst_n ),
.sccb_write_req (sccb_write_req ),
.sccb_read_req (sccb_read_req ),
.sccb_ack (sccb_ack ),
.lut_id (lut_id ),
.lut_addr (lut_addr ),
.lut_data (lut_data ),
.sccb_sda (sccb_sda ),
.sccb_scl (sccb_scl )
);
*/
localparam cmoscnt_max = 'd2000;
localparam cmoscnt_half = 'd1000;
localparam comscnt_quater = 'd1500;
localparam IDLE = 8'b00000000;
localparam START = 8'b00000001;
localparam ID_ADDR = 8'b00000010;
localparam REG_ADDR = 8'b00000100;
localparam WRITE = 8'b00001000;
localparam READ = 8'b00010000;
localparam STOP = 8'b00100000;
wire sccb_en;
wire scl_down;
reg scl_frame;
reg sdo;
reg sdir;
reg turnflag;
reg [7:0] read_data;//
reg [15:0] delay;//最大值必须是cmoscnt_max的两倍以上
reg [7:0] ID_CNT; //发送从机地址
reg [7:0] REG_CNT; //发送寄存器地址
reg [7:0] DATA_CNT;//读写地址
reg [15:0] sccb_cnt;
reg [7:0] currentstate;
reg [7:0] nextstate;
assign sccb_en = (sccb_cnt == cmoscnt_max)?1'b1:1'b0;
assign scl_down = (!sccb_scl && scl_frame)?1'b1:1'b0;
assign sccb_sda = (sdir)?sdo:1'hz;
assign sccb_ack = (delay == 16'd5000)?1'b1:1'b0;
assign sccb_read_data = read_data;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
sccb_cnt <= 'd0;
else if(currentstate == IDLE)
sccb_cnt <= 'd0;
else if(currentstate == STOP)
begin
if(sccb_scl == 1'b1 && sdo == 1'b1)
sccb_cnt <= sccb_cnt;
else
sccb_cnt <= (sccb_cnt < 2*cmoscnt_max)?sccb_cnt + 1'b1:sccb_cnt;
end
// else if(currentstate == START)
// begin
// if(turnflag)begin
// sccb_cnt <= (sccb_cnt > cmoscnt_half)?'d0:(sccb_cnt + 1'b1);
// turnflag <= 1'b0;
// end
// else
// sccb_cnt <= sccb_cnt + 1'b1;
// end
else if(sccb_cnt == cmoscnt_max)
sccb_cnt <= 'd0;
else
sccb_cnt <= sccb_cnt + 1'b1;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
scl_frame <= 1'b1;
else
scl_frame <= sccb_scl;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
sccb_scl <= 1'b1;
else if(currentstate == IDLE)
sccb_scl <= 1'b1;
else if(currentstate == START)
begin
if(sccb_cnt == comscnt_quater)
sccb_scl <= 1'b0;
else
sccb_scl <= sccb_scl;
end
else if(currentstate == STOP)
begin
if(sccb_en)
sccb_scl <= 1'b1;
else
sccb_scl <= sccb_scl;
end
else if(sccb_en)
sccb_scl <= ~sccb_scl;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
turnflag <= 1'b0;
else if(currentstate == STOP && nextstate == START)
turnflag <= 1'b1;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
ID_CNT <= 'd0;
REG_CNT <= 'd0;
DATA_CNT<= 'd0;
end
else begin
ID_CNT <= (currentstate == ID_ADDR)?((sccb_en && sccb_scl)?(ID_CNT + 1'b1):ID_CNT):'d0;
REG_CNT <= (currentstate == REG_ADDR)?((sccb_en && sccb_scl)?(REG_CNT + 1'b1):REG_CNT):'d0;
DATA_CNT <= (currentstate == WRITE || currentstate == READ)?((sccb_en && sccb_scl)?(DATA_CNT + 1'b1):DATA_CNT):'d0;
end
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
sdir <= 1'b1;
else if (sccb_scl == 1'b0 && sccb_cnt == cmoscnt_half)begin
case(currentstate)
IDLE: sdir <= 1'b1;
START: sdir <= 1'b1;
ID_ADDR: sdir <= (ID_CNT == 8'd8)?1'b0:1'b1;
REG_ADDR: sdir <= (REG_CNT == 8'd8 || REG_CNT == 8'd17)?1'b0:1'b1;
WRITE: sdir <= (DATA_CNT == 8'd8)?1'b0:1'b1;
READ: sdir <= (DATA_CNT == 8'd8)?1'b1:1'b0;
STOP: sdir <= 1'b1;//(bit_cnt == )?1'b0:1'b1;
default: sdir <= 1'b1;
endcase
end
end
always @(posedge clk or negedge rst_n)
begin
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
delay <= 'd0;
else if(currentstate == STOP)
delay <= delay + 1'b1;
else
delay <= 'd0;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
currentstate <= IDLE;
else
currentstate <= nextstate;
end
always @(*)
begin
case(currentstate)
IDLE:begin
if(sccb_read_req || sccb_write_req)
nextstate = START;
else
nextstate = IDLE;
end
START:begin
if(sccb_read_req || sccb_write_req)begin
if(sccb_scl == 1'b0 && sccb_cnt == cmoscnt_max)
nextstate = ID_ADDR;
else
nextstate = START;
end
else
nextstate = IDLE;
end
ID_ADDR:begin
if(sccb_read_req || sccb_write_req)begin
if(ID_CNT == 8'd9)
nextstate = REG_ADDR;
else
nextstate = ID_ADDR;
end
else
nextstate = IDLE;
end
REG_ADDR:begin
if(sccb_read_req || sccb_write_req)begin
if(REG_CNT == 8'd18)begin
if(sccb_write_req)
nextstate = WRITE;
else
nextstate = READ;
end
else
nextstate = REG_ADDR;
end
else
nextstate = IDLE;
end
WRITE:begin
if(sccb_read_req || sccb_write_req)begin
if(DATA_CNT == 8'd9)
nextstate = STOP;
else
nextstate = WRITE;
end
else
nextstate = IDLE;
end
READ:begin
if(sccb_read_req || sccb_write_req)begin
if(DATA_CNT == 8'd9)
nextstate = STOP;
else
nextstate = READ;
end
else
nextstate = IDLE;
end
STOP:begin
// if(delay == 'd5000)begin
// if(sccb_read_req || sccb_write_req)
// nextstate = START;
// else
// end
if(delay == 'd5000)
nextstate = IDLE;
else
nextstate = STOP;
end
default:;
endcase
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
sdo <= 1'b1;
read_data <= 'd0;
end
else begin
case(currentstate)
IDLE:begin
sdo <= 1'b1;
end
START:begin
if(sccb_cnt == cmoscnt_half)
sdo <= 1'b0;
else
sdo <= sdo;
end
ID_ADDR:begin
if(ID_CNT < 8'd7)begin
if(sccb_scl == 1'b0 && sccb_cnt == cmoscnt_half)
sdo <= lut_id[8'd6 - ID_CNT];
else
sdo <= sdo;
end
else begin
if(sccb_scl == 1'b0 && sccb_cnt == cmoscnt_half)
begin
if(sccb_write_req)
sdo <= 1'b0;
else
sdo <= 1'b1;
end
else
sdo <= sdo;
end
end
REG_ADDR:begin
if(REG_CNT < 8'd8)begin
if(sccb_scl == 1'b0 && sccb_cnt == cmoscnt_half)
sdo <= lut_addr[8'd15 - REG_CNT];//高8位
else
sdo <= sdo;
end
else if(REG_CNT < 8'd17 && REG_CNT > 8'd8)begin
if(sccb_scl == 1'b0 && sccb_cnt == cmoscnt_half)
sdo <= lut_addr[8'd16 - REG_CNT];//低8位
else
sdo <= sdo;
end
else
sdo <= 1'b1;
end
WRITE:begin
if(DATA_CNT < 8'd8 )begin
if(sccb_scl == 1'b0 && sccb_cnt == cmoscnt_half)
sdo <= lut_data[8'd7 - DATA_CNT];
else
sdo <= sdo;
end
else
sdo <= 1'b1;
end
READ:begin
if(DATA_CNT < 8'd8 )begin
if(sccb_scl == 1'b1 && sccb_cnt == cmoscnt_half)
read_data[8'd7 - DATA_CNT] <= sccb_sda;
else
read_data <= read_data;
end
else begin
read_data <= read_data;
if(DATA_CNT == 8'd8)
sdo <= 1'b0;
else
sdo <= sdo;
end
end
STOP:begin
if(sccb_scl == 1'b1 && sccb_cnt == 2*cmoscnt_max)
sdo <= 1'b1;
else
sdo <= 1'b0;
end
default:;
endcase
end
end
endmodule