diff --git a/docs/03_usb_interface.md b/docs/03_usb_interface.md index 8b85b06..c8f6eff 100644 --- a/docs/03_usb_interface.md +++ b/docs/03_usb_interface.md @@ -328,18 +328,18 @@ Use this command to set value of persistent setting option. Available persistent _This command does not require arguments or data._ #### `response` (time) -| offset | type | description | -| ------ | ------- | ------------------------------------ | -| `0` | uint8_t | Weekday (1 - 7), 1 represents Monday | -| `1` | uint8_t | Hours (0 - 23) | -| `2` | uint8_t | Minutes (0 - 59) | -| `3` | uint8_t | Seconds (0 - 59) | -| `4` | uint8_t | _Unused_ (returns zero) | -| `5` | uint8_t | Year (0 - 99) | -| `6` | uint8_t | Month (1 - 12) | -| `7` | uint8_t | Day (1 - 31) | +| offset | type | description | +| ------ | ------- | ----------------------------------------- | +| `0` | uint8_t | Weekday (1 - 7), 1 represents Monday | +| `1` | uint8_t | Hours (0 - 23) | +| `2` | uint8_t | Minutes (0 - 59) | +| `3` | uint8_t | Seconds (0 - 59) | +| `4` | uint8_t | Century (0 - 255), 0 represents year 1900 | +| `5` | uint8_t | Year (0 - 99) | +| `6` | uint8_t | Month (1 - 12) | +| `7` | uint8_t | Day (1 - 31) | -Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decimal) format. +Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decimal) format, except for the century field. --- @@ -356,16 +356,16 @@ Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decima | `[7:0]` | Seconds (0 - 59) | #### `arg1` (time_1) -| bits | description | -| --------- | -------------- | -| `[31:24]` | _Unused_ | -| `[23:16]` | Year (0 - 99) | -| `[15:8]` | Month (1 - 12) | -| `[7:0]` | Day (1 - 31) | +| bits | description | +| --------- | ----------------------------------------- | +| `[31:24]` | Century (0 - 255), 0 represents year 1900 | +| `[23:16]` | Year (0 - 99) | +| `[15:8]` | Month (1 - 12) | +| `[7:0]` | Day (1 - 31) | _This command does not send response data._ -Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decimal) format. +Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decimal) format, except for the century field. --- diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv index 139fa62..a0540d5 100644 --- a/fw/rtl/mcu/mcu_top.sv +++ b/fw/rtl/mcu/mcu_top.sv @@ -506,7 +506,7 @@ module mcu_top ( REG_RTC_TIME_1: begin reg_rdata <= { - 8'd0, + 7'd0, n64_scb.rtc_rdata[42], n64_scb.rtc_rdata[41:34], 3'd0, n64_scb.rtc_rdata[33:29], 2'd0, n64_scb.rtc_rdata[25:20] @@ -849,6 +849,7 @@ module mcu_top ( REG_RTC_TIME_1: begin n64_scb.rtc_wdata_valid <= 1'b1; + n64_scb.rtc_wdata[42] <= reg_wdata[24]; n64_scb.rtc_wdata[41:34] <= reg_wdata[23:16]; n64_scb.rtc_wdata[33:29] <= reg_wdata[12:8]; n64_scb.rtc_wdata[25:20] <= reg_wdata[5:0]; diff --git a/fw/rtl/n64/n64_scb.sv b/fw/rtl/n64/n64_scb.sv index 1fecb12..ca5d9ff 100644 --- a/fw/rtl/n64/n64_scb.sv +++ b/fw/rtl/n64/n64_scb.sv @@ -40,8 +40,8 @@ interface n64_scb (); logic rtc_pending; logic rtc_done; logic rtc_wdata_valid; - logic [41:0] rtc_rdata; - logic [41:0] rtc_wdata; + logic [42:0] rtc_rdata; + logic [42:0] rtc_wdata; logic cfg_unlock; logic cfg_pending; diff --git a/fw/rtl/n64/n64_si.sv b/fw/rtl/n64/n64_si.sv index 73875a4..0b5a79f 100644 --- a/fw/rtl/n64/n64_si.sv +++ b/fw/rtl/n64/n64_si.sv @@ -362,6 +362,7 @@ module n64_si ( logic [2:0] rtc_time_weekday; logic [4:0] rtc_time_month; logic [7:0] rtc_time_year; + logic rtc_time_century; always_ff @(posedge clk) begin if (reset) begin @@ -377,6 +378,7 @@ module n64_si ( if (!(|rtc_stopped) && !n64_scb.rtc_pending && n64_scb.rtc_wdata_valid && (tx_state != TX_STATE_DATA)) begin { + rtc_time_century, rtc_time_year, rtc_time_month, rtc_time_weekday, @@ -408,6 +410,7 @@ module n64_si ( 4'd5: rtc_time_weekday <= rx_byte_data[2:0]; 4'd6: rtc_time_month <= rx_byte_data[4:0]; 4'd7: rtc_time_year <= rx_byte_data; + 4'd8: rtc_time_century <= rx_byte_data[0]; endcase end end @@ -415,6 +418,7 @@ module n64_si ( always_comb begin n64_scb.rtc_rdata = { + rtc_time_century, rtc_time_year, rtc_time_month, rtc_time_weekday, @@ -469,7 +473,7 @@ module n64_si ( 4'd4: tx_byte_data = {5'd0, rtc_time_weekday}; 4'd5: tx_byte_data = {3'd0, rtc_time_month}; 4'd6: tx_byte_data = rtc_time_year; - 4'd7: tx_byte_data = 8'h01; + 4'd7: tx_byte_data = {7'd0, rtc_time_century}; 4'd8: tx_byte_data = {(|rtc_stopped), 7'd0}; endcase end diff --git a/sw/bootloader/src/sc64.c b/sw/bootloader/src/sc64.c index d233b51..0ccaa89 100644 --- a/sw/bootloader/src/sc64.c +++ b/sw/bootloader/src/sc64.c @@ -322,10 +322,11 @@ sc64_error_t sc64_get_time (sc64_rtc_time_t *t) { .id = CMD_ID_TIME_GET }; sc64_error_t error = sc64_execute_cmd(&cmd); + t->weekday = ((cmd.rsp[0] >> 24) & 0xFF); t->hour = ((cmd.rsp[0] >> 16) & 0xFF); t->minute = ((cmd.rsp[0] >> 8) & 0xFF); t->second = (cmd.rsp[0] & 0xFF); - t->weekday = ((cmd.rsp[1] >> 24) & 0xFF); + t->century = ((cmd.rsp[1] >> 24) & 0xFF); t->year = ((cmd.rsp[1] >> 16) & 0xFF); t->month = ((cmd.rsp[1] >> 8) & 0xFF); t->day = (cmd.rsp[1] & 0xFF); @@ -334,11 +335,12 @@ sc64_error_t sc64_get_time (sc64_rtc_time_t *t) { sc64_error_t sc64_set_time (sc64_rtc_time_t *t) { uint32_t time[2] = {( + ((t->weekday << 24) & 0xFF) | ((t->hour << 16) & 0xFF) | ((t->minute << 8) & 0xFF) | (t->second & 0xFF) ), ( - ((t->weekday << 24) & 0xFF) | + ((t->century << 24) & 0xFF) | ((t->year << 16) & 0xFF) | ((t->month << 8) & 0xFF) | (t->day & 0xFF) diff --git a/sw/bootloader/src/sc64.h b/sw/bootloader/src/sc64.h index abc5137..d1a601e 100644 --- a/sw/bootloader/src/sc64.h +++ b/sw/bootloader/src/sc64.h @@ -149,6 +149,7 @@ typedef struct { uint8_t day; uint8_t month; uint8_t year; + uint8_t century; } sc64_rtc_time_t; typedef enum { diff --git a/sw/bootloader/src/test.c b/sw/bootloader/src/test.c index c8d3621..f3017c8 100644 --- a/sw/bootloader/src/test.c +++ b/sw/bootloader/src/test.c @@ -110,7 +110,7 @@ static void test_sc64_cfg (void) { } display_printf("RTC current time:\n "); - display_printf("%04d-%02d-%02d", 2000 + FROM_BCD(t.year), FROM_BCD(t.month), FROM_BCD(t.day)); + display_printf("%04d-%02d-%02d", 1900 + (t.century * 100) + FROM_BCD(t.year), FROM_BCD(t.month), FROM_BCD(t.day)); display_printf("T"); display_printf("%02d:%02d:%02d", FROM_BCD(t.hour), FROM_BCD(t.minute), FROM_BCD(t.second)); display_printf(" (%s)", weekdays[FROM_BCD(t.weekday)]); diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c index 4be4da1..64edd21 100644 --- a/sw/controller/src/cfg.c +++ b/sw/controller/src/cfg.c @@ -491,8 +491,8 @@ save_type_t cfg_get_save_type (void) { void cfg_get_time (uint32_t *args) { rtc_time_t t; rtc_get_time(&t); - args[0] = ((t.hour << 16) | (t.minute << 8) | t.second); - args[1] = ((t.weekday << 24) | (t.year << 16) | (t.month << 8) | t.day); + args[0] = ((t.weekday << 24) | (t.hour << 16) | (t.minute << 8) | t.second); + args[1] = ((t.century << 24) | (t.year << 16) | (t.month << 8) | t.day); } void cfg_set_time (uint32_t *args) { @@ -504,6 +504,7 @@ void cfg_set_time (uint32_t *args) { t.day = (args[1] & 0xFF); t.month = ((args[1] >> 8) & 0xFF); t.year = ((args[1] >> 16) & 0xFF); + t.century = ((args[1] >> 24) & 0xFF); rtc_set_time(&t); } diff --git a/sw/controller/src/dd.c b/sw/controller/src/dd.c index 0f6be8e..e7d609d 100644 --- a/sw/controller/src/dd.c +++ b/sw/controller/src/dd.c @@ -365,6 +365,7 @@ void dd_process (void) { case DD_CMD_SET_RTC_MINUTE_SECOND: p.time.minute = ((data >> 8) & 0xFF); p.time.second = (data & 0xFF); + p.time.century = (p.time.year >= 0x96) ? 0 : 1; rtc_set_time(&p.time); break; diff --git a/sw/controller/src/rtc.c b/sw/controller/src/rtc.c index 6d898d5..a731ba2 100644 --- a/sw/controller/src/rtc.c +++ b/sw/controller/src/rtc.c @@ -20,6 +20,8 @@ #define RTC_ADDRESS_SRAM_REGION (0x24) #define RTC_ADDRESS_SRAM_VERSION (0x28) #define RTC_ADDRESS_SRAM_SETTINGS (0x2C) +#define RTC_ADDRESS_SRAM_CENTURY (0x40) +#define RTC_ADDRESS_SRAM_LAST_YEAR (0x41) #define RTC_RTCSEC_ST (1 << 7) @@ -37,8 +39,9 @@ static rtc_time_t rtc_time = { .hour = 0x12, .weekday = 0x01, .day = 0x01, - .month = 0x01, - .year = 0x24 + .month = 0x06, + .year = 0x24, + .century = 0x01, }; static bool rtc_time_pending = false; @@ -102,10 +105,17 @@ static void rtc_osc_stop (void) { static void rtc_read_time (void) { uint8_t regs[7]; + uint8_t last_year; if (rtc_read(RTC_ADDRESS_RTCSEC, regs, 7)) { return; } + if (rtc_read(RTC_ADDRESS_SRAM_CENTURY, &rtc_time.century, 1)) { + return; + } + if (rtc_read(RTC_ADDRESS_SRAM_LAST_YEAR, &last_year, 1)) { + return; + } rtc_sanitize_time(regs); @@ -116,6 +126,15 @@ static void rtc_read_time (void) { rtc_time.day = regs[4]; rtc_time.month = regs[5]; rtc_time.year = regs[6]; + + if (rtc_time.year < last_year) { + rtc_time.century += 1; + rtc_write(RTC_ADDRESS_SRAM_CENTURY, &rtc_time.century, 1); + } + + if (rtc_time.year != last_year) { + rtc_write(RTC_ADDRESS_SRAM_LAST_YEAR, &rtc_time.year, 1); + } } static void rtc_write_time (void) { @@ -136,8 +155,12 @@ static void rtc_write_time (void) { regs[0] |= RTC_RTCSEC_ST; regs[3] |= RTC_RTCWKDAY_VBATEN; + rtc_write(RTC_ADDRESS_SRAM_CENTURY, &rtc_time.century, 1); + rtc_write(RTC_ADDRESS_SRAM_LAST_YEAR, &rtc_time.year, 1); rtc_write(RTC_ADDRESS_RTCMIN, ®s[1], 6); rtc_write(RTC_ADDRESS_RTCSEC, ®s[0], 1); + + rtc_read_time(); } static void rtc_read_region (void) { @@ -165,6 +188,7 @@ void rtc_get_time (rtc_time_t *time) { time->day = rtc_time.day; time->month = rtc_time.month; time->year = rtc_time.year; + time->century = rtc_time.century; } void rtc_set_time (rtc_time_t *time) { @@ -175,6 +199,7 @@ void rtc_set_time (rtc_time_t *time) { rtc_time.day = time->day; rtc_time.month = time->month; rtc_time.year = time->year; + rtc_time.century = time->century; rtc_time_pending = true; } @@ -254,6 +279,7 @@ void rtc_process (void) { rtc_time.hour = ((data[0] >> 16) & 0xFF); rtc_time.minute = ((data[0] >> 8) & 0xFF); rtc_time.second = ((data[0] >> 0) & 0xFF); + rtc_time.century = ((data[1] >> 24) & 0xFF); rtc_time.year = ((data[1] >> 16) & 0xFF); rtc_time.month = ((data[1] >> 8) & 0xFF); rtc_time.day = ((data[1] >> 0) & 0xFF); @@ -293,6 +319,7 @@ void rtc_process (void) { (rtc_time.second << 0) ); data[1] = ( + (rtc_time.century << 24) | (rtc_time.year << 16) | (rtc_time.month << 8) | (rtc_time.day << 0) diff --git a/sw/controller/src/rtc.h b/sw/controller/src/rtc.h index a8714d9..6a53a3f 100644 --- a/sw/controller/src/rtc.h +++ b/sw/controller/src/rtc.h @@ -14,6 +14,7 @@ typedef struct { uint8_t day; uint8_t month; uint8_t year; + uint8_t century; } rtc_time_t; typedef struct { diff --git a/sw/deployer/src/main.rs b/sw/deployer/src/main.rs index 15af878..a346caa 100644 --- a/sw/deployer/src/main.rs +++ b/sw/deployer/src/main.rs @@ -781,7 +781,7 @@ fn handle_set_command(connection: Connection, command: &SetCommands) -> Result<( match command { SetCommands::Rtc => { - let datetime = Local::now(); + let datetime = Local::now().naive_local(); sc64.set_datetime(datetime)?; println!( "SC64 RTC datetime synchronized to: {}", diff --git a/sw/deployer/src/sc64/mod.rs b/sw/deployer/src/sc64/mod.rs index aa44584..a1ac764 100644 --- a/sw/deployer/src/sc64/mod.rs +++ b/sw/deployer/src/sc64/mod.rs @@ -25,7 +25,7 @@ use self::{ get_config, get_setting, Config, ConfigId, FirmwareStatus, Setting, SettingId, UpdateStatus, }, }; -use chrono::{DateTime, Local}; +use chrono::NaiveDateTime; use rand::Rng; use std::{ cmp::min, @@ -55,7 +55,7 @@ pub struct DeviceState { pub button_mode: ButtonMode, pub rom_extended_enable: Switch, pub led_enable: Switch, - pub datetime: DateTime, + pub datetime: NaiveDateTime, pub fpga_debug_data: FpgaDebugData, pub diagnostic_data: DiagnosticData, } @@ -210,7 +210,7 @@ impl SC64 { Ok(()) } - fn command_time_get(&mut self) -> Result, Error> { + fn command_time_get(&mut self) -> Result { let data = self.link.execute_command(&Command { id: b't', args: [0, 0], @@ -224,7 +224,7 @@ impl SC64 { Ok(convert_to_datetime(&data[0..8].try_into().unwrap())?) } - fn command_time_set(&mut self, datetime: DateTime) -> Result<(), Error> { + fn command_time_set(&mut self, datetime: NaiveDateTime) -> Result<(), Error> { self.link.execute_command(&Command { id: b'T', args: convert_from_datetime(datetime), @@ -529,11 +529,11 @@ impl SC64 { self.command_config_set(Config::TvType(tv_type)) } - pub fn get_datetime(&mut self) -> Result, Error> { + pub fn get_datetime(&mut self) -> Result { self.command_time_get() } - pub fn set_datetime(&mut self, datetime: DateTime) -> Result<(), Error> { + pub fn set_datetime(&mut self, datetime: NaiveDateTime) -> Result<(), Error> { self.command_time_set(datetime) } diff --git a/sw/deployer/src/sc64/time.rs b/sw/deployer/src/sc64/time.rs index 5e2c36f..871adc0 100644 --- a/sw/deployer/src/sc64/time.rs +++ b/sw/deployer/src/sc64/time.rs @@ -1,5 +1,5 @@ use super::Error; -use chrono::{DateTime, Datelike, Local, NaiveDateTime, TimeZone, Timelike}; +use chrono::{Datelike, NaiveDateTime, Timelike}; pub fn u8_from_bcd(value: u8) -> u8 { (((value & 0xF0) >> 4) * 10) + (value & 0x0F) @@ -9,31 +9,31 @@ pub fn bcd_from_u8(value: u8) -> u8 { (((value / 10) & 0x0F) << 4) | ((value % 10) & 0x0F) } -pub fn convert_to_datetime(data: &[u8; 8]) -> Result, Error> { - let hour = u8_from_bcd(data[1]); - let minute = u8_from_bcd(data[2]); +pub fn convert_to_datetime(data: &[u8; 8]) -> Result { let second = u8_from_bcd(data[3]); - let year = u8_from_bcd(data[5]) as u32 + 2000; - let month = u8_from_bcd(data[6]); + let minute = u8_from_bcd(data[2]); + let hour = u8_from_bcd(data[1]); let day = u8_from_bcd(data[7]); - let native = &NaiveDateTime::parse_from_str( - &format!("{year:02}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"), + let month = u8_from_bcd(data[6]); + let year = 1900u32 + (data[4] as u32 * 100) + u8_from_bcd(data[5]) as u32; + NaiveDateTime::parse_from_str( + &format!("{year:4}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"), "%Y-%m-%dT%H:%M:%S", ) - .map_err(|_| Error::new("Couldn't convert from bytes to DateTime"))?; - Ok(Local.from_local_datetime(native).unwrap()) + .map_err(|_| Error::new("Couldn't convert from bytes to NaiveDateTime")) } -pub fn convert_from_datetime(datetime: DateTime) -> [u32; 2] { - let weekday = bcd_from_u8((datetime.weekday() as u8) + 1); - let hour = bcd_from_u8(datetime.hour() as u8); - let minute = bcd_from_u8(datetime.minute() as u8); +pub fn convert_from_datetime(datetime: NaiveDateTime) -> [u32; 2] { let second = bcd_from_u8(datetime.second() as u8); - let year = bcd_from_u8((datetime.year() - 2000) as u8); - let month = bcd_from_u8(datetime.month() as u8); + let minute = bcd_from_u8(datetime.minute() as u8); + let hour = bcd_from_u8(datetime.hour() as u8); + let weekday = bcd_from_u8((datetime.weekday() as u8) + 1); let day = bcd_from_u8(datetime.day() as u8); + let month = bcd_from_u8(datetime.month() as u8); + let year = bcd_from_u8((datetime.year() % 100) as u8); + let century = ((datetime.year() - 1900) / 100) as u8; [ u32::from_be_bytes([weekday, hour, minute, second]), - u32::from_be_bytes([0, year, month, day]), + u32::from_be_bytes([century, year, month, day]), ] }