// Bit definitions: // 31 Read / not write. // 30 Repeated Start. On read cycles setting this bit uses repeated start instad // 29:23 Reserved. // 22:16 7-bit I2C address of slave. // 15:8 Register Subaddress // 7:0 Data to write. Don't care on read cycles. //reg [31:0] status; // I2C status register // Bit definitions // 31 Busy. Not ready to accept new control register writes. // 30 Address NACK. Last address cycle resulted in no acknowledge from slave. // 29 Data NACK. Last data write cycle resulted in no acknowledge from slave. // 28 Read. Most recently completed cycle was a read. Data in bits 7:0 is valid. // 27 Overrun. An attempt was made to write the ctrl_reg while busy. Cleared // on successful write of ctrl_reg. // 26 Initializing - waiting for SDA to go high after module reset // 25:8 Reserved. Tied to zero. // 7:0 Read data. Valid after a read cycle when bit 28 is set.
@emard I integrated your i2c module into the cortex (see https://gitlab.com/pnru/ulx3s-misc/-/blob/master/tmp/sys.v). It does not seem to work for me, but maybe I'm using it wrong. I also - and I really should not have - went down the rabbit hole and wrote an I2C master from scratch (see https://gitlab.com/pnru/ulx3s-misc/-/blob/master/tmp/i2c.v). It also does not seem to work for me.
When I look at the wave forms in Icarus/GTKWave I think both bits of code are producing very similar signals that should work (at least in my current understanding or I2C and the MCP7940). Your code is more careful to debounce the SCL and SDA signals. It also seems to have an I2C bus reset that I don't have (Start followed by Stop, with no clocks in between.) A 3rd difference is that I'm running SCL at ~400KHz and you are is using ~1Mhz
I really need to get back to real work, but if someone wants to take a look at and do a code or GTKWave review over the next days, I'd be grateful.
Thanks! I've combined your logic with my 2 level state machine into a new, short module: https://gitlab.com/pnru/ulx3s-misc/-/blob/master/tmp/i2c.v
Upon reflection I think that your 1 register at the time approach is more practical then copying the whole register file. I've now given it a two 16-bit word interface that resembles the interfaces found on 1970's minicomputers.
I think - but I'm prejudiced - that this source code it somewhat easier to read and extend then most other examples. Extending this controller to one that also does multi-byte reads and writes should not be more than 50 lines of code, as would be adding more esoteric features such as clock stretching and bus arbitration - for a total of ~300 lines of plain Verilog. However, for now I like it as a simple 200 line controller. I would expect that it works as-is when used with vendor tools, but I have not tried.
I've integrated the new I2C controller with the Cortex model. Its two registers are addressed at 0xfec0 (CMD/STS) and 0xfec2 (ADR). Repository is here: https://gitlab.com/pnru/cortex
I've also written a quick and dirty
hwclock program. It supports the three core options:
-r: read the RTC clock and print its current time,
-w: write the RTC clock from system time; this also initializes the RTC chip,
-s: set the system time to the RTC time; if the RTC is not running it aborts (to prevent time going backwards). Normally one would have
hwclock -s in the
/etc/rc boot script. I will put it all in a new Cortex release in due course.
The source for the
hwclock program is here: https://gitlab.com/pnru/ulx3s-misc/-/blob/master/tmp/hwclock.c
By the way, I noticed that the ESP32 initialization program sets one alarm active and also sets a drift correction. I think these may be left overs from another project, as neither makes sense.
@emard I've set my terminal emulator to have a 10ms inter-character delay and that seems to work well for pasting code. Probably 5ms works as well. I am an absolute layman when it comes to oscillator xtal's, but I think there is a significant effect of manufacturing tolerances? I think maybe something like +/- 20 ppm (a few seconds per day)? I think if you test 3 random boards from the recent production batch you will find three different drifts. It is also temperature dependent, but that is just a few ppm for normal usage.
On PC's the RTC varied from PC to PC and the
hwclock program has an automatic mechanism to adjust the drift compensation (see "Adjust function" section). When the RTC time is updated it checks by how many seconds since the last update and recalculates the drift value. However, that only makes sense when the RTC is used in a fixed PC context, not where is shared between a multitude of experimental FPGA cores. Hence my thought was to leave the drift setting unchanged. Maybe the
hwclock program needs a
-d option to set the drift correction explicitly when the user wants to.