from nmigen import * from nmigen_boards.ulx3s import * class Blinky(Elaboratable): def elaborate(self, platform): led = platform.request("led", 0) timer = Signal(26) m = Module() m.d.sync += timer.eq(timer + 1) m.d.comb += led.o.eq(timer[-1]) return m if __name__ == "__main__": platform = ULX3S_85F_Platform() platform.build(Blinky(), do_program=True)
@lawrie @emard I don't intend to sound demanding, although probably I do. It is just that with summer behind us I have to focus on the day job as well.
My plan is to make it similar to Linux on an (retro) PC. The AT used the MC146818 chip and I'm sure that an equivalent is still hiding in the north/south bridges. On Linux you have the
hwclock command to copy between the RTC chip and the system clock. See man page here. I will add a /dev/rtc device driver that reads/writes the RTC chip and add a hwclock program. This program can then be put in the /etc/rc boot script.
The MC146818 is a good model, but I don't like the multiplexed AD bus too much. I like the MM58167 better as a model. All these chips are the same: they provide a bank of byte sized registers with the time, etc. I think the MCP790 is still the same (I'm often surprised how little has changed in 40 years, other than size/speed/bloat increasing by a factor of 1000.)
Maybe the module interface should be something like:
module RTC ( input wire clk, // prefer 25 or 125Mhz // retro CPU interface input wire [5:0] ab, // 4,5,6, etc. wires all the same to me. input wire [7:0] di, output wire [7:0] do, input wire csn, input wire wrn, input wire rdn, // I2C to RTC chip inout wire scl, inout wire sda );
How many registers there are, or what data/command is in which register does not really matter to me: that is all easily handed in the
/dev/rtc device driver (it will probably read the registers and convert to a string, and vice versa when writing; maybe converting to a time value is even easier).
I am not sure what the best policy is for syncing between the FPGA and the RTC chip. My first gut feel would be to have a duplicate set of registers in the FPGA and to refresh those autonomously each second (maybe 10x per second). If the duplicate register block had its dirty bit set (i.e. was written to) then the copy would be in the other direction.
rc boot script would read /dev/rtc and set the system time from it if it was ahead of system time. If system time is ahead of /dev/rtc it would write the system time to the RTC. To get the ball rolling the user would have to set the right date once. When I get TCP/IP working on the FPGA Cortex it can read out an NTP server and use that as a time base.
/dev/rtcdevice 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.
// 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.