aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@trantor.hos.anvin.org>2008-12-28 15:38:41 -0800
committerH. Peter Anvin <hpa@trantor.hos.anvin.org>2008-12-28 15:38:41 -0800
commit700e44bac30699b4652fe1c56bba287b1da9dd77 (patch)
treee702de51675d95d4617ba03f7aca75dfeb6df3e4
parenta20c73a0afc2a87ce959dfe75c8b27ac8c6ca2a4 (diff)
downloadabc80-700e44bac30699b4652fe1c56bba287b1da9dd77.tar.gz
abc80-700e44bac30699b4652fe1c56bba287b1da9dd77.tar.xz
abc80-700e44bac30699b4652fe1c56bba287b1da9dd77.zip
i2c: much more compact design
Mostly as an exercise in hardware optimization, recode the i2c generator for much smaller size. It is down from 76 to 43 LEs with the new design.
-rw-r--r--i2c.v146
1 files changed, 96 insertions, 50 deletions
diff --git a/i2c.v b/i2c.v
index 0ba67a1..4e98c8f 100644
--- a/i2c.v
+++ b/i2c.v
@@ -32,19 +32,19 @@ module sound_i2c (
reg [3:0] i2c_rom_a;
wire [8:0] i2c_rom_q;
- reg [26:0] i2c_bit_shr;
- reg [3:0] i2c_scl_out;
- reg [3:0] i2c_sda_out;
reg [4:0] i2c_bit_ctr;
reg [1:0] i2c_qtr;
+ reg abn;
+ reg bit;
+ reg scl;
reg i2c_active = 1'b0;
wire i2c_stop = i2c_rom_a == i2c_rom_stop;
// Output drivers: all outputs are open drain
- assign i2c_scl = i2c_scl_out[3] ? 1'bz : 1'b0;
- assign i2c_sda = i2c_sda_out[3] ? 1'bz : 1'b0;
+ assign i2c_scl = scl ? 1'bz : 1'b0;
+ assign i2c_sda = bit ? 1'bz : 1'b0;
sound_i2c_rom sound_i2c_rom (
.clk (clk),
@@ -52,81 +52,127 @@ module sound_i2c (
.q (i2c_rom_q)
);
- // We send packets of 3x9 = 27 bits, plus S and P conditions.
-
+ //
+ // We generate four types of symbols: "normal", where we assert SCL,
+ // and "abnormal", where we don't. An S or Sr condition is composed of
+ // A1 N0 and a P condition of A0 A1. A1 is the idle state.
+ //
always @(posedge clk)
if ( ~rst_n )
begin
// Synchronous reset
i2c_rom_a <= 4'd0;
- i2c_bit_shr <= 27'hxxxx;
i2c_bit_ctr <= 5'd0;
i2c_qtr <= 2'b00;
i2c_active <= 1'b1;
- i2c_scl_out <= 4'b1111;
- i2c_sda_out <= 4'b1111;
+ scl <= 1'b1;
+ bit <= 1'b1;
+ abn <= 1'b1;
end
- else if ( clk_en & i2c_active & (~i2c_scl_out[3] | i2c_scl) )
+ else if ( clk_en & i2c_active & (~scl | i2c_scl) )
begin
- i2c_scl_out <= { i2c_scl_out[2:0], 1'b1 };
- i2c_sda_out <= { i2c_sda_out[2:0], 1'b1 };
i2c_qtr <= i2c_qtr + 1;
- if ( i2c_qtr == 2'b00 )
+ // Phase 1: set SCL to 1; phase 3: set SCL to abn
+ if (i2c_qtr[0])
+ scl <= abn | ~i2c_qtr[1];
+
+ // Phase 0: load a new data bit, set up abn
+ if (i2c_qtr == 2'b00)
begin
i2c_bit_ctr <= i2c_bit_ctr + 1;
- i2c_bit_shr <= { i2c_bit_shr[25:0], 1'bx };
-
- casex ( i2c_bit_ctr )
- 5'b00000:
+
+ case ( i2c_bit_ctr )
+ // Start condition
+ 5'd0:
+ {abn, bit} <= 2'b11; // A1
+ 5'd1:
+ {abn, bit} <= 2'b00; // N0
+ // First byte (I2C address)
+ 5'd2:
+ {abn, bit} <= {1'b0, i2c_addr[6]};
+ 5'd3:
+ {abn, bit} <= {1'b0, i2c_addr[5]};
+ 5'd4:
+ {abn, bit} <= {1'b0, i2c_addr[4]};
+ 5'd5:
+ {abn, bit} <= {1'b0, i2c_addr[3]};
+ 5'd6:
+ {abn, bit} <= {1'b0, i2c_addr[2]};
+ 5'd7:
+ {abn, bit} <= {1'b0, i2c_addr[1]};
+ 5'd8:
+ {abn, bit} <= {1'b0, i2c_addr[0]};
+ 5'd9:
+ {abn, bit} <= 2'b00; // Write
+ 5'd10:
+ {abn, bit} <= 2'b01; // ACK
+ // Second byte (device register address + D8)
+ 5'd11, 5'd12, 5'd13:
+ {abn, bit} <= 2'b00;
+ 5'd14:
+ {abn, bit} <= {1'b0, i2c_rom_a[3]};
+ 5'd15:
+ {abn, bit} <= {1'b0, i2c_rom_a[2]};
+ 5'd16:
+ {abn, bit} <= {1'b0, i2c_rom_a[1]};
+ 5'd17:
+ {abn, bit} <= {1'b0, i2c_rom_a[0]};
+ 5'd18:
+ {abn, bit} <= {1'b0, i2c_rom_q[8]};
+ 5'd19:
+ {abn, bit} <= 2'b01; // ACK
+ // Third byte (D7-D0)
+ 5'd20:
+ {abn, bit} <= {1'b0, i2c_rom_q[7]};
+ 5'd21:
+ {abn, bit} <= {1'b0, i2c_rom_q[6]};
+ 5'd22:
+ {abn, bit} <= {1'b0, i2c_rom_q[5]};
+ 5'd23:
+ {abn, bit} <= {1'b0, i2c_rom_q[4]};
+ 5'd24:
+ {abn, bit} <= {1'b0, i2c_rom_q[3]};
+ 5'd25:
+ {abn, bit} <= {1'b0, i2c_rom_q[2]};
+ 5'd26:
+ {abn, bit} <= {1'b0, i2c_rom_q[1]};
+ 5'd27:
+ {abn, bit} <= {1'b0, i2c_rom_q[0]};
+ 5'd28:
+ {abn, bit} <= 2'b01; // ACK
+ // Stop condition
+ 5'd29:
+ {abn, bit} <= 2'b10; // A0
+ 5'd30:
+ {abn, bit} <= 2'b11; // A1
+ // Idle cycle and prepare for the next loop
+ 5'd31:
begin
- // Start condition, prepare for data transfer
- i2c_scl_out <= 4'b1110;
- i2c_sda_out <= 4'b1000;
- i2c_bit_shr <= { i2c_addr, 1'b0, 1'b1,
- 3'b000, i2c_rom_a, i2c_rom_q[8], 1'b1,
- i2c_rom_q[7:0], 1'b1 };
- i2c_rom_a <= i2c_rom_a + 1;
- i2c_active <= ~i2c_stop;
+ {abn, bit} <= 2'b11; // A1 = idle
+ i2c_rom_a <= i2c_rom_a + 1;
+ i2c_active <= ~i2c_stop;
end
- 5'b111xx:
- begin
- // Stop condition
- i2c_scl_out <= 4'b0111;
- i2c_sda_out <= 4'b0001;
- i2c_bit_ctr <= 5'd0;
- end
- default:
- begin
- // Send data bit
- i2c_scl_out <= 4'b0110;
- i2c_sda_out <= {4{i2c_bit_shr[26]}};
- end
- endcase // casex( i2c_bit_ctr )
- end // if ( i2c_qtr == 2'b00 )
+ endcase // case( i2c_bit_ctr )
+ end // if (i2c_qtr == 2'b00)
end // if ( clk_en & i2c_active & (~i2c_scl_out[3] | i2c_scl) )
endmodule // sound_i2c
-// It should be possible to infer a ROM from this construct, but
-// since the number of inputs are so small, the synthesizer probably
-// uses LUTs as ROMs instead.
+// Asynchronous ROM. If this is made larger, it should probably
+// be a synchronous ROM instead.
module sound_i2c_rom (
input clk,
input [3:0] a,
output [8:0] q
);
- reg [3:0] a_reg;
reg [8:0] q_reg;
assign q = q_reg;
- always @(posedge clk)
- a_reg <= a;
-
- always @(posedge clk)
- case (a_reg)
+ always @(*)
+ case (a)
4'h0: q_reg <= 9'h01a;
4'h1: q_reg <= 9'h01a;
4'h2: q_reg <= 9'h07b;