Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    emard
    @emard
    Your retro-pc interface is easiest for me to implement, so OS will then take care of race condition of reading when it changes. For example say you first read 59 minutes and next reading you read incremented hour, so instead of 01:00 you have 01:59, more than hour later :). I don't have to fix in the core, OS will
    emard
    @emard
    is "wire [5:0] ab" the address for RTC's register? there are 32 RTC registers 0x00-0x1F and small general purpose RAM 0x20-0x5F; so [6:0] should be sufficient here
    Paul Ruiz
    @pnru_gitlab
    The MM58167 offers a rollover status bit, but I can live without that. The /dev/rtc device driver can read the registers twice in succession and if the two values are not the same repeat the double read until they are. As the time value on ancient Unix only has 1 second resolution, I don't really need to read the faster registers.
    is "wire [5:0] ab" the address for RTC's register? Yes
    emard
    @emard
    I can make such module you need
    except rollover bit unless MCP datasheet somehow provides it, I didn't study much in detail
    emard
    @emard
    datasheet says the rollover can be avoided by multiple-register read from a single i2c command which I think currently i2c master i borrowed from some forum doesn't have, it does each byte separately hmm
    OK let have something kinda-working first and fix details later :)
    emard
    @emard
    Looking again at your module interface, it seems that I must circular-read all registers and buffer in FPGA registers and be ready to deliver content any time immediately as CPU sets rdn=0? There's not async handshake line to wait for slow i2c to complete the read?
    Paul Ruiz
    @pnru_gitlab
    Yes, there can be a ready signal that stretches the read until data is ready (very similar to DTACK on an 68K). Use with reason, though. As long as ready is low interrupts are not serviced and sdram refresh is paused as well.
    emard
    @emard
    Then I'd rather buffer RTC's 32 bytes than let everything else wait for reading the time.
    Paul Ruiz
    @pnru_gitlab
    It is thinking these type of things through that is so time consuming. At the moment I'm thinking that an autonomous "circular" read is the simplest, maybe using a block read for all registers.
    It is the writing that concerns me: what if a CPU write happens at the same time as the circular read?
    I currently don't have an idea to solve that simply and elegantly.
    emard
    @emard
    I would make priority to write, once module sees write it will buffer its addr/value, halt reads, complete write of requested single byte and continue with circular read
    Maybe we can introduce a simple cache inbetween. So cache would immediately return value. I must also notify unreliable RTC bugs, we can't be really shure that any write has made it until i2c reads it back
    So I'd say first strategy is better: write will halt circular read, make write and circual will resume. CPU must read back to make sure it looks ok after a write
    emard
    @emard
    So, setting alarm or new time will be some CPU intensive, while just reading a time will be really quick
    Paul Ruiz
    @pnru_gitlab
    Sounds a bit complicated, to be honest. How about a ready bit in a status register? It would split each second in three parts. During the first part RDY is true and the CPU can read/write. During the second part RDY is false, but the CPU can still read/write (to deal with the CPU reading RDY just before it switches off). During the third part the I2C controller reads the RTC registers - or, if the registers were modified in the 1st/2nd phase, it writes the registers back. Wouldn't that be much simpler?
    Paul Ruiz
    @pnru_gitlab
    Maybe to make it even more rigid and simple, the state machine should have 4 fixed phases: RDY, RDY grace, write-back, read. The write-back cycle would be disabled if there was no write during phase 1 and 2. This was the I2C controller has 250ms for writing, 250ms for reading and the CPU knows that after reading RDY active, it has at least 250ms to complete its read/write.
    If the controller was using 32 byte block reads/writes, the rollover problem would be solved as well.
    emard
    @emard
    How about just a simple 8-bit interface to i2c master? It will have 4 byte registers to write to make a request and 4 byte to read to get response. It can be reduced to 1 byte to write and 1 byte read if rest of address and directions are set
    emard
    @emard
    // 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.
    Paul Ruiz
    @pnru_gitlab
    It is a good place to start. We can check that with EVMBUG and short programs and see how it goes. Always good to start simple: first single register read/write, then whole bank read/write and finally retro chip emulation.
    emard
    @emard
    I will then make 8-bit interface to i2c master and will test it a bit. For retro chip emulation we need to rethink design to make it properly. MCP has some interrupt flags and may behave specific, for example circular read may unwantedly clear interrupt flags or similar
    Kristin Davidson
    @aphistic
    @lawrie i have been interested in learning and HDL but i've been overwhelmed by the options and i'm kinda in analysis paralysis mode right now. chisel seems neat, i was leaning toward vhdl at one point for a reason i don't remember. verilog seems to be most popular for the risc-v cores i've seen and spinalhdl intrigues me for the same reason chisel does
    emard
    @emard
    @pnru_gitlab here is 8-bit master interface. I haven't tested it very simple by reading seconds and seems ok https://github.com/emard/ulx3s-misc/blob/master/examples/rtc/i2c_master/proj/hdl/i2c_master_8bit.v
    Lawrie Griffiths
    @lawrie
    @aphistic verilog and vhdl are the lower level languages that the others generate. Verilog is directly supported by the open source tool chain but Vhdl is supported via the GHDL Yosys plug in. Only the simplest Risc-v cores (such as picorv32) are written directly in Verilog. The others use one of the higher level languages which generate Verilog. Personally, I mainly use Verilog and SpinalHDL. The reason I learnt SpinalHDL is because Vexriscv is written in it.
    I would recommend starting with Verilog and picorv32.
    Lawrie Griffiths
    @lawrie
    I have had to learn Vhdl as a lot of cores are written in it, but I mainly read it and convert it to Verilog.
    emard
    @emard
    when we are at verilog, I have found on the net several i2c bidirectional bridges. Only one code worked at our board but it works only if compiled by diamond. trellis compiles but doesn't work. Can someone take a look what I have missed: bridge: https://github.com/emard/ulx3s-misc/blob/master/examples/rtc/micropython-mcp7940n/proj/hdl/i2c_bridge.v toplevel usage: https://github.com/emard/ulx3s-misc/blob/master/examples/rtc/micropython-mcp7940n/proj/top/top_i2c_bridge.v
    Kristin Davidson
    @aphistic
    cool, thanks for the insights both of you! I'll start playing around with picorv32 and verilog, then probably move on to spinalhdl once i'm more comfortable with verilog
    since it'll be a month or so before my ulx3s gets here i'll have to start with the tinyfpga bx's i have sitting around :)
    Lawrie Griffiths
    @lawrie
    I did quite a few projects on the TinyFPGA BX using picosoc. The first one to start with is the simple one in the examples - https://github.com/tinyfpga/TinyFPGA-BX/tree/master/examples/picosoc
    Paul Ruiz
    @pnru_gitlab

    @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.

    If all else fails, I will route SCL and SDA to the side headers and attach a logic analyser, so that I can see what the RTC chip actually responds.
    emard
    @emard
    Doesn't work mean you get 0xFF or 0x00 values reading all registers? I'll check pullup/pulldown sensitivity maybe reason is simple
    emard
    @emard
    for me it works for any gpdi_scl/sda PULLMODE=UP, DOWN, or NONE so it's not pull setting. I can prepare a bitstream and micropython to ulx3s-bin example that initializes the clock setting time from NTP
    emard
    @emard
    https://github.com/emard/ulx3s-bin/tree/master/esp32/micropython/rtc can you try this RTC esp32 example for NTP setting? >>> rtcdemo.mcp.time should advance seconds
    Paul Ruiz
    @pnru_gitlab
    @emard your esp32 test above passes: it initializes the mcp7940 and starts it running. The experiments with the Cortex result in 0x00 in every register being reported. Am I correct to think that your master8bit example does not respond to a NACK from the RTC chip but ignores it? Is that significant?
    Paul Ruiz
    @pnru_gitlab
    Hmmm.... when I run the Cortex with your i2c code after the ESP32 test it gives non-zero registers. Maybe it is an issue with properly initializing the RTC chip.
    Paul Ruiz
    @pnru_gitlab
    But my home-brew driver still shows all zeroes.
    emard
    @emard
    That's why I gave it my i2c brdge demo, for start it's easier to have RTC initialized and running. Battery also helps keeping the settings. Uninitialized RTC doesn't tick but still should not be 0 to everything. I ignored nack. I write request to read and wait for busy to become 0. Then I can read response
    Paul Ruiz
    @pnru_gitlab
    Yeah, I guess I should get that battery installed...
    emard
    @emard
    RTC traffic also appears at GPDI connector (if there's i2c chip 3.3V->5V adapter soldered on board PCA9306D). Some monitors may hold i2c lines. Also gpdi connector can be used to monitor traffic if e.g. you have hdmi breakout board
    If you don't have battery, RTC will keep setting until board is powered off
    If you have 2 ULX3S boards and mini-display OLED/LCD, you can connect 2 ULX3S together and on one run scopeio, which will monitor the other i2c traffic
    I can prepare scope stuff for such setup because scopeio is vhdl, advanced so much that ghdl won't have chance in near future to compile it
    emard
    @emard
    UPS but we have problem there will be 2 RTC chips colliding address :)
    about i2c master 8bit. First write highest bytes 3,2,1 (order not important) and last write byte 0, this should initiate i2c transaction. byte 3=0x80 is READ, byte 3=0x00 is write