(I hope you have a 'MCP3021', around 2 posts from now, it should already work...)
Nope, not yet. I want to simulate using an EEPROM 24LC08 with known content for now.
Datasheet here: http://ww1.microchip.com/downloads/en/devicedoc/21710k.pdf
Well, we will still start with it since it is far simpler than the eeprom which needs the ee slaves master address, then subaddress, then with a read, another I2C_start & that master address again, in read mode, then read the data...
For the 'MCP3021', you present it's address in read mode, then receive 2 bytes, every time.
Don't worry, we will make progress very fast.
Ok, for your code, the ' (
!Conv && !Sequence_busy) '. I know how with TTL logic, and a lot of input button pullups, you can easily make the mistake of wanting negative logic on the input of your modules.
Remember, this module will eventually be buried in logic and controlled by other logic. By default, for example, if you use our 'ADC_rdy' output to feed and Altera fifo or ram module's 'wren' input to accumulate a read cache, if we made it negative logic, you would need to invert that signal's 'WIRE' between modules.
Personally, even for reset I prefer positive logic. Here is how I deal with negative logic inputs:
Now, if you prefer to keep your module's 'negative' logic inputs, let me know know. We will change the Rst input in the module back to Rst_n, and change the Conv input to a Conv_n so when you wire this module into a larger design, when feeding it's input ports, you know that these controls are negative.
After simulation your design, here is what I get:
Step #1 patches: (please update your code so we match)
module MCP3021_Reader (
Clk, // System clock
Rst, // General reset active low
SCL_in, // I2C serial cloc input. Is this necessary for single master?
SDA_in, // I2C serial data input
Conv, // Start acquisition
SCL_out, // Serial clock output
SDA_out, // Serial data output
SCL_oe, // Output enable for SCL
SDA_oe, // Output enable for SDA
ADC_rdy, // ADC_rdy output - strobes high when data is available
ADC_val // 10 bit ADC register. ADC data is available in this register when ADC_Rdy
);
input Clk;
input Rst;
input SCL_in;
input SDA_in;
input Conv;
output reg SCL_out;
output reg SDA_out;
output reg SCL_oe;
output reg SDA_oe;
output reg ADC_rdy;
output reg [9:0] ADC_val;
reg[15:0] I2C_Period = 16'd0; // I2C clock divider
reg[3:0] Sequence_counter;
reg Sequence_busy;
parameter CLK_IN_HZ = 16; //25'd24000000; // System clock frequency
parameter I2C_SCL_HZ = 4; // I2C SCL frequency (= 120 for 100KHz or 30 for 400KHz)
localparam I2C_PERIOD_SIZE = (CLK_IN_HZ/I2C_SCL_HZ)-1 ;
localparam Sequence_size = 10;
always @ (posedge Clk)
begin
if (Rst)
begin
//I2C_Period <= I2C_PERIOD_SIZE[15:0] ; // This would add a delay after Conv has been asserted before the I2C transaction begins
I2C_Period <= 1'd0 ; // This would begin the I2C transaction immediately
ADC_rdy <= 1'd0 ;
SCL_out <= 1'd1; // I2C default state is all HIGH
SCL_oe <= 1'd0; // make I2C tristate by default.
SDA_out <= 1'd1; // I2C default state is all HIGH
SDA_oe <= 1'd0; // make I2C tristate by default.
ADC_rdy <= 1'd0;
Sequence_busy <= 1'd0;
Sequence_counter <= 1'd0;
end
if (Conv && !Sequence_busy)
begin
Sequence_busy <= 1'd1;
Sequence_counter <= Sequence_size[3:0];
end
else
if(Sequence_busy)
begin
if (I2C_Period == 16'd0)
begin
SCL_out <= 1'd1; // I2C default state is all HIGH
SCL_oe <= 1'd1; // make I2C tristate by default.
SDA_out <= 1'd1; // I2C default state is all HIGH
SDA_oe <= 1'd1; // make I2C tristate by default.
ADC_rdy <= ~ADC_rdy;
I2C_Period <= I2C_PERIOD_SIZE[15:0];
if (ADC_rdy)
begin
Sequence_counter <= Sequence_counter - 1'd1;
end
if (Sequence_counter == 4'd0)
begin
Sequence_busy <= 1'd0;
ADC_rdy <= 1'd0;
end
end
else
begin
I2C_Period <= I2C_Period - 1'd1;
end
end
end // posedge clk
endmodule
#1, I made the module's control inputs positive logic.
#2, I shrank the I2C period counter to 16 bits.
#3, To get rid of compiler warnings, I added these -> I2C_Period <= I2C_PERIOD_SIZE
[15:0];
and everywhere here -> ADC_rdy <=
1'd0;
as well as -> if (Sequence_counter ==
4'd0)
All throughout the code. It is good to avoid pesky compiler warnings all over the place...
Now, you made the inner and outer portions of your loop's behavior backwards. This is fine since I didn't specify how we would be using the sequencer, though the hint was that I said a sequence count of '20' would cycle the ADC_rdy only 10 times. In I2C, we want to specify actions at the I2C SCL_out's rising and falling edge, so we need the specify a sequence counter which has events at each time. In fact, we may want 4x the period events. IE, perform an action at the rise and another action at the fall of the SCL_out output, as well as another action right in the middle on the SCL_out being low and in the middle of it being high as well.
Next post in around an hour with a solution...
Here is a hint:
For the 2x approach, SCL_out <= Sequence_counter[0];
For the 4x approach, SCL_out <= Sequence_counter[1];
Within the main looping sequencer of your code.
When defining the size of the sequences, the Sequence_counter = (sequence size *2 or *4) and the preiod counter will be divided by 2 or 4 so that the SCL_out will have the correct speed.
Let me check the I2C data sheets. The x2 method should be fine, however, I need to double check how clock stretching works since I've only used it once so long ago. We might just need to go with the x4 method unless you want the x4 just so you have an additional timing slice you may take advantage of if you ever need it.