MacPlus_MiSTer/sys/i2c.v
2021-03-03 17:35:46 +08:00

104 lines
2.2 KiB
Verilog

module i2c
(
input CLK,
input START,
input READ,
input [6:0] I2C_ADDR,
input I2C_WLEN, // 0 - one byte, 1 - two bytes
input [7:0] I2C_WDATA1,
input [7:0] I2C_WDATA2,
output [7:0] I2C_RDATA,
output reg END = 1,
output reg ACK = 0,
//I2C bus
output I2C_SCL,
inout I2C_SDA
);
// Clock Setting
parameter CLK_Freq = 50_000_000; // 50 MHz
parameter I2C_Freq = 400_000; // 400 KHz
localparam I2C_FreqX2 = I2C_Freq*2;
reg I2C_CLOCK;
reg [31:0] cnt;
wire [31:0] cnt_next = cnt + I2C_FreqX2;
always @(posedge CLK) begin
cnt <= cnt_next;
if(cnt_next >= CLK_Freq) begin
cnt <= cnt_next - CLK_Freq;
I2C_CLOCK <= ~I2C_CLOCK;
end
end
assign I2C_SCL = (SCLK | I2C_CLOCK) ? 1'bZ : 1'b0;
assign I2C_SDA = SDO[3] ? 1'bz : 1'b0;
reg SCLK;
reg [3:0] SDO;
reg [0:7] rdata;
reg [5:0] SD_COUNTER;
reg [0:31] SD;
initial begin
SD_COUNTER = 'b111111;
SD = 'hFFFF;
SCLK = 1;
SDO = 4'b1111;
end
assign I2C_RDATA = rdata;
always @(posedge CLK) begin
reg old_clk;
reg old_st;
reg rd,len;
old_clk <= I2C_CLOCK;
old_st <= START;
// delay to make sure SDA changed while SCL is stabilized at low
if(old_clk && ~I2C_CLOCK && ~SD_COUNTER[5]) SDO[0] <= SD[SD_COUNTER[4:0]];
SDO[3:1] <= SDO[2:0];
if(~old_st && START) begin
SCLK <= 1;
SDO <= 4'b1111;
ACK <= 0;
END <= 0;
rd <= READ;
len <= I2C_WLEN;
if(READ) SD <= {2'b10, I2C_ADDR, 1'b1, 1'b1, 8'b11111111, 1'b0, 3'b011, 9'b111111111};
else SD <= {2'b10, I2C_ADDR, 1'b0, 1'b1, I2C_WDATA1, 1'b1, I2C_WDATA2, 4'b1011};
SD_COUNTER <= 0;
end else begin
if(~old_clk && I2C_CLOCK && ~&SD_COUNTER) begin
SD_COUNTER <= SD_COUNTER + 6'd1;
case(SD_COUNTER)
01: SCLK <= 0;
10: ACK <= ACK | I2C_SDA;
19: if(~rd) begin
ACK <= ACK | I2C_SDA;
if(~len) SD_COUNTER <= 29;
end
20: if(rd) SCLK <= 1;
23: if(rd) END <= 1;
28: if(~rd) ACK <= ACK | I2C_SDA;
29: if(~rd) SCLK <= 1;
32: if(~rd) END <= 1;
endcase
if(SD_COUNTER >= 11 && SD_COUNTER <= 18) rdata[SD_COUNTER[4:0]-11] <= I2C_SDA;
end
end
end
endmodule