SummerCart64/fw/rtl/cpu/cpu_i2c.sv

156 lines
4.3 KiB
Systemverilog

module cpu_i2c (
if_system.sys sys,
if_cpu_bus bus,
output i2c_scl,
inout i2c_sda
);
reg [1:0] state;
reg mack;
reg [8:0] trx_data;
always_comb begin
bus.rdata = 32'd0;
if (bus.ack) begin
case (bus.address[2])
0: bus.rdata = {27'd0, |state, ~trx_data[0], mack, 2'b00};
1: bus.rdata = {23'd0, trx_data[0], trx_data[8:1]};
default: bus.rdata = 32'd0;
endcase
end
end
always_ff @(posedge sys.clk) begin
bus.ack <= 1'b0;
if (bus.request) begin
bus.ack <= 1'b1;
end
if (sys.reset) begin
mack <= 1'b0;
end else if (bus.request && bus.wmask[0] && !bus.address[2]) begin
mack <= bus.wdata[2];
end
end
reg [5:0] clock_div;
reg [3:0] clock_phase_gen;
wire clock_tick = &clock_div;
wire [3:0] clock_phase = {4{clock_tick}} & clock_phase_gen;
always_ff @(posedge sys.clk) begin
if (sys.reset) begin
clock_div <= 6'd0;
end else begin
clock_div <= clock_div + 1'd1;
end
if (sys.reset || state == 2'd0) begin
clock_phase_gen <= 4'b0001;
end else if (clock_tick) begin
clock_phase_gen <= {clock_phase_gen[2:0], clock_phase_gen[3]};
end
end
reg [3:0] bit_counter;
reg sda_i_ff1, sda_i_ff2;
reg scl_o;
reg sda_o;
assign i2c_scl = scl_o ? 1'bZ : 1'b0;
assign i2c_sda = sda_o ? 1'bZ : 1'b0;
always_ff @(posedge sys.clk) begin
{sda_i_ff2, sda_i_ff1} <= {sda_i_ff1, i2c_sda};
if (sys.reset) begin
state <= 2'd0;
scl_o <= 1'b1;
sda_o <= 1'b1;
end else begin
case (state)
2'd0: begin
bit_counter <= 4'd0;
if (bus.request && bus.wmask[0]) begin
case (bus.address[2])
0: begin
if (bus.wdata[1]) state <= 2'd2;
if (bus.wdata[0]) state <= 2'd1;
end
1: begin
state <= 2'd3;
trx_data <= {bus.wdata[7:0], ~mack};
end
endcase
end
end
2'd1: begin
if (clock_phase[0]) begin
scl_o <= 1'b1;
sda_o <= 1'b1;
end
if (clock_phase[1]) begin
sda_o <= 1'b0;
end
if (clock_phase[3]) begin
state <= 2'd0;
scl_o <= 1'b0;
end
end
2'd2: begin
if (clock_phase[0]) begin
scl_o <= 1'b0;
sda_o <= 1'b0;
end
if (clock_phase[1]) begin
scl_o <= 1'b1;
end
if (clock_phase[3]) begin
state <= 2'd0;
sda_o <= 1'b1;
end
end
2'd3: begin
if (clock_phase[0]) begin
bit_counter <= bit_counter + 1'd1;
scl_o <= 1'b0;
sda_o <= trx_data[8];
end
if (clock_phase[1]) begin
scl_o <= 1'b1;
end
if (clock_phase[3]) begin
trx_data <= {trx_data[7:0], sda_i_ff2};
scl_o <= 1'b0;
end
if (bit_counter == 4'b1010) begin
state <= 2'd0;
end
end
default: begin
state <= 2'd0;
scl_o <= 1'b1;
sda_o <= 1'b1;
end
endcase
end
end
endmodule