[SC64][FW][SW] Added option to set and read century when updating RTC datetime

This commit is contained in:
Mateusz Faderewski 2024-06-06 23:00:38 +02:00
parent 6bbfee44e7
commit e4af127e55
14 changed files with 91 additions and 53 deletions

View File

@ -328,18 +328,18 @@ Use this command to set value of persistent setting option. Available persistent
_This command does not require arguments or data._ _This command does not require arguments or data._
#### `response` (time) #### `response` (time)
| offset | type | description | | offset | type | description |
| ------ | ------- | ------------------------------------ | | ------ | ------- | ----------------------------------------- |
| `0` | uint8_t | Weekday (1 - 7), 1 represents Monday | | `0` | uint8_t | Weekday (1 - 7), 1 represents Monday |
| `1` | uint8_t | Hours (0 - 23) | | `1` | uint8_t | Hours (0 - 23) |
| `2` | uint8_t | Minutes (0 - 59) | | `2` | uint8_t | Minutes (0 - 59) |
| `3` | uint8_t | Seconds (0 - 59) | | `3` | uint8_t | Seconds (0 - 59) |
| `4` | uint8_t | _Unused_ (returns zero) | | `4` | uint8_t | Century (0 - 255), 0 represents year 1900 |
| `5` | uint8_t | Year (0 - 99) | | `5` | uint8_t | Year (0 - 99) |
| `6` | uint8_t | Month (1 - 12) | | `6` | uint8_t | Month (1 - 12) |
| `7` | uint8_t | Day (1 - 31) | | `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) | | `[7:0]` | Seconds (0 - 59) |
#### `arg1` (time_1) #### `arg1` (time_1)
| bits | description | | bits | description |
| --------- | -------------- | | --------- | ----------------------------------------- |
| `[31:24]` | _Unused_ | | `[31:24]` | Century (0 - 255), 0 represents year 1900 |
| `[23:16]` | Year (0 - 99) | | `[23:16]` | Year (0 - 99) |
| `[15:8]` | Month (1 - 12) | | `[15:8]` | Month (1 - 12) |
| `[7:0]` | Day (1 - 31) | | `[7:0]` | Day (1 - 31) |
_This command does not send response data._ _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.
--- ---

View File

@ -506,7 +506,7 @@ module mcu_top (
REG_RTC_TIME_1: begin REG_RTC_TIME_1: begin
reg_rdata <= { reg_rdata <= {
8'd0, 7'd0, n64_scb.rtc_rdata[42],
n64_scb.rtc_rdata[41:34], n64_scb.rtc_rdata[41:34],
3'd0, n64_scb.rtc_rdata[33:29], 3'd0, n64_scb.rtc_rdata[33:29],
2'd0, n64_scb.rtc_rdata[25:20] 2'd0, n64_scb.rtc_rdata[25:20]
@ -849,6 +849,7 @@ module mcu_top (
REG_RTC_TIME_1: begin REG_RTC_TIME_1: begin
n64_scb.rtc_wdata_valid <= 1'b1; 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[41:34] <= reg_wdata[23:16];
n64_scb.rtc_wdata[33:29] <= reg_wdata[12:8]; n64_scb.rtc_wdata[33:29] <= reg_wdata[12:8];
n64_scb.rtc_wdata[25:20] <= reg_wdata[5:0]; n64_scb.rtc_wdata[25:20] <= reg_wdata[5:0];

View File

@ -40,8 +40,8 @@ interface n64_scb ();
logic rtc_pending; logic rtc_pending;
logic rtc_done; logic rtc_done;
logic rtc_wdata_valid; logic rtc_wdata_valid;
logic [41:0] rtc_rdata; logic [42:0] rtc_rdata;
logic [41:0] rtc_wdata; logic [42:0] rtc_wdata;
logic cfg_unlock; logic cfg_unlock;
logic cfg_pending; logic cfg_pending;

View File

@ -362,6 +362,7 @@ module n64_si (
logic [2:0] rtc_time_weekday; logic [2:0] rtc_time_weekday;
logic [4:0] rtc_time_month; logic [4:0] rtc_time_month;
logic [7:0] rtc_time_year; logic [7:0] rtc_time_year;
logic rtc_time_century;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (reset) 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 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_year,
rtc_time_month, rtc_time_month,
rtc_time_weekday, rtc_time_weekday,
@ -408,6 +410,7 @@ module n64_si (
4'd5: rtc_time_weekday <= rx_byte_data[2:0]; 4'd5: rtc_time_weekday <= rx_byte_data[2:0];
4'd6: rtc_time_month <= rx_byte_data[4:0]; 4'd6: rtc_time_month <= rx_byte_data[4:0];
4'd7: rtc_time_year <= rx_byte_data; 4'd7: rtc_time_year <= rx_byte_data;
4'd8: rtc_time_century <= rx_byte_data[0];
endcase endcase
end end
end end
@ -415,6 +418,7 @@ module n64_si (
always_comb begin always_comb begin
n64_scb.rtc_rdata = { n64_scb.rtc_rdata = {
rtc_time_century,
rtc_time_year, rtc_time_year,
rtc_time_month, rtc_time_month,
rtc_time_weekday, rtc_time_weekday,
@ -469,7 +473,7 @@ module n64_si (
4'd4: tx_byte_data = {5'd0, rtc_time_weekday}; 4'd4: tx_byte_data = {5'd0, rtc_time_weekday};
4'd5: tx_byte_data = {3'd0, rtc_time_month}; 4'd5: tx_byte_data = {3'd0, rtc_time_month};
4'd6: tx_byte_data = rtc_time_year; 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}; 4'd8: tx_byte_data = {(|rtc_stopped), 7'd0};
endcase endcase
end end

View File

@ -263,10 +263,11 @@ sc64_error_t sc64_get_time (sc64_rtc_time_t *t) {
.id = CMD_ID_TIME_GET .id = CMD_ID_TIME_GET
}; };
sc64_error_t error = sc64_execute_cmd(&cmd); sc64_error_t error = sc64_execute_cmd(&cmd);
t->weekday = ((cmd.rsp[0] >> 24) & 0xFF);
t->hour = ((cmd.rsp[0] >> 16) & 0xFF); t->hour = ((cmd.rsp[0] >> 16) & 0xFF);
t->minute = ((cmd.rsp[0] >> 8) & 0xFF); t->minute = ((cmd.rsp[0] >> 8) & 0xFF);
t->second = (cmd.rsp[0] & 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->year = ((cmd.rsp[1] >> 16) & 0xFF);
t->month = ((cmd.rsp[1] >> 8) & 0xFF); t->month = ((cmd.rsp[1] >> 8) & 0xFF);
t->day = (cmd.rsp[1] & 0xFF); t->day = (cmd.rsp[1] & 0xFF);
@ -275,11 +276,12 @@ sc64_error_t sc64_get_time (sc64_rtc_time_t *t) {
sc64_error_t sc64_set_time (sc64_rtc_time_t *t) { sc64_error_t sc64_set_time (sc64_rtc_time_t *t) {
uint32_t time[2] = {( uint32_t time[2] = {(
((t->weekday << 24) & 0xFF) |
((t->hour << 16) & 0xFF) | ((t->hour << 16) & 0xFF) |
((t->minute << 8) & 0xFF) | ((t->minute << 8) & 0xFF) |
(t->second & 0xFF) (t->second & 0xFF)
), ( ), (
((t->weekday << 24) & 0xFF) | ((t->century << 24) & 0xFF) |
((t->year << 16) & 0xFF) | ((t->year << 16) & 0xFF) |
((t->month << 8) & 0xFF) | ((t->month << 8) & 0xFF) |
(t->day & 0xFF) (t->day & 0xFF)

View File

@ -149,6 +149,7 @@ typedef struct {
uint8_t day; uint8_t day;
uint8_t month; uint8_t month;
uint8_t year; uint8_t year;
uint8_t century;
} sc64_rtc_time_t; } sc64_rtc_time_t;
typedef enum { typedef enum {

View File

@ -110,7 +110,7 @@ static void test_sc64_cfg (void) {
} }
display_printf("RTC current time:\n "); 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("T");
display_printf("%02d:%02d:%02d", FROM_BCD(t.hour), FROM_BCD(t.minute), FROM_BCD(t.second)); 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)]); display_printf(" (%s)", weekdays[FROM_BCD(t.weekday)]);

View File

@ -437,8 +437,8 @@ save_type_t cfg_get_save_type (void) {
void cfg_get_time (uint32_t *args) { void cfg_get_time (uint32_t *args) {
rtc_time_t t; rtc_time_t t;
rtc_get_time(&t); rtc_get_time(&t);
args[0] = ((t.hour << 16) | (t.minute << 8) | t.second); args[0] = ((t.weekday << 24) | (t.hour << 16) | (t.minute << 8) | t.second);
args[1] = ((t.weekday << 24) | (t.year << 16) | (t.month << 8) | t.day); args[1] = ((t.century << 24) | (t.year << 16) | (t.month << 8) | t.day);
} }
void cfg_set_time (uint32_t *args) { void cfg_set_time (uint32_t *args) {
@ -450,6 +450,7 @@ void cfg_set_time (uint32_t *args) {
t.day = (args[1] & 0xFF); t.day = (args[1] & 0xFF);
t.month = ((args[1] >> 8) & 0xFF); t.month = ((args[1] >> 8) & 0xFF);
t.year = ((args[1] >> 16) & 0xFF); t.year = ((args[1] >> 16) & 0xFF);
t.century = ((args[1] >> 24) & 0xFF);
rtc_set_time(&t); rtc_set_time(&t);
} }

View File

@ -365,6 +365,7 @@ void dd_process (void) {
case DD_CMD_SET_RTC_MINUTE_SECOND: case DD_CMD_SET_RTC_MINUTE_SECOND:
p.time.minute = ((data >> 8) & 0xFF); p.time.minute = ((data >> 8) & 0xFF);
p.time.second = (data & 0xFF); p.time.second = (data & 0xFF);
p.time.century = (p.time.year >= 0x96) ? 0 : 1;
rtc_set_time(&p.time); rtc_set_time(&p.time);
break; break;

View File

@ -20,6 +20,8 @@
#define RTC_ADDRESS_SRAM_REGION (0x24) #define RTC_ADDRESS_SRAM_REGION (0x24)
#define RTC_ADDRESS_SRAM_VERSION (0x28) #define RTC_ADDRESS_SRAM_VERSION (0x28)
#define RTC_ADDRESS_SRAM_SETTINGS (0x2C) #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) #define RTC_RTCSEC_ST (1 << 7)
@ -37,8 +39,9 @@ static rtc_time_t rtc_time = {
.hour = 0x12, .hour = 0x12,
.weekday = 0x01, .weekday = 0x01,
.day = 0x01, .day = 0x01,
.month = 0x01, .month = 0x06,
.year = 0x24 .year = 0x24,
.century = 0x01,
}; };
static bool rtc_time_pending = false; static bool rtc_time_pending = false;
@ -102,10 +105,17 @@ static void rtc_osc_stop (void) {
static void rtc_read_time (void) { static void rtc_read_time (void) {
uint8_t regs[7]; uint8_t regs[7];
uint8_t last_year;
if (rtc_read(RTC_ADDRESS_RTCSEC, regs, 7)) { if (rtc_read(RTC_ADDRESS_RTCSEC, regs, 7)) {
return; 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); rtc_sanitize_time(regs);
@ -116,6 +126,15 @@ static void rtc_read_time (void) {
rtc_time.day = regs[4]; rtc_time.day = regs[4];
rtc_time.month = regs[5]; rtc_time.month = regs[5];
rtc_time.year = regs[6]; 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) { static void rtc_write_time (void) {
@ -136,8 +155,12 @@ static void rtc_write_time (void) {
regs[0] |= RTC_RTCSEC_ST; regs[0] |= RTC_RTCSEC_ST;
regs[3] |= RTC_RTCWKDAY_VBATEN; 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, &regs[1], 6); rtc_write(RTC_ADDRESS_RTCMIN, &regs[1], 6);
rtc_write(RTC_ADDRESS_RTCSEC, &regs[0], 1); rtc_write(RTC_ADDRESS_RTCSEC, &regs[0], 1);
rtc_read_time();
} }
static void rtc_read_region (void) { static void rtc_read_region (void) {
@ -165,6 +188,7 @@ void rtc_get_time (rtc_time_t *time) {
time->day = rtc_time.day; time->day = rtc_time.day;
time->month = rtc_time.month; time->month = rtc_time.month;
time->year = rtc_time.year; time->year = rtc_time.year;
time->century = rtc_time.century;
} }
void rtc_set_time (rtc_time_t *time) { 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.day = time->day;
rtc_time.month = time->month; rtc_time.month = time->month;
rtc_time.year = time->year; rtc_time.year = time->year;
rtc_time.century = time->century;
rtc_time_pending = true; rtc_time_pending = true;
} }
@ -254,6 +279,7 @@ void rtc_process (void) {
rtc_time.hour = ((data[0] >> 16) & 0xFF); rtc_time.hour = ((data[0] >> 16) & 0xFF);
rtc_time.minute = ((data[0] >> 8) & 0xFF); rtc_time.minute = ((data[0] >> 8) & 0xFF);
rtc_time.second = ((data[0] >> 0) & 0xFF); rtc_time.second = ((data[0] >> 0) & 0xFF);
rtc_time.century = ((data[1] >> 24) & 0xFF);
rtc_time.year = ((data[1] >> 16) & 0xFF); rtc_time.year = ((data[1] >> 16) & 0xFF);
rtc_time.month = ((data[1] >> 8) & 0xFF); rtc_time.month = ((data[1] >> 8) & 0xFF);
rtc_time.day = ((data[1] >> 0) & 0xFF); rtc_time.day = ((data[1] >> 0) & 0xFF);
@ -293,6 +319,7 @@ void rtc_process (void) {
(rtc_time.second << 0) (rtc_time.second << 0)
); );
data[1] = ( data[1] = (
(rtc_time.century << 24) |
(rtc_time.year << 16) | (rtc_time.year << 16) |
(rtc_time.month << 8) | (rtc_time.month << 8) |
(rtc_time.day << 0) (rtc_time.day << 0)

View File

@ -14,6 +14,7 @@ typedef struct {
uint8_t day; uint8_t day;
uint8_t month; uint8_t month;
uint8_t year; uint8_t year;
uint8_t century;
} rtc_time_t; } rtc_time_t;
typedef struct { typedef struct {

View File

@ -781,7 +781,7 @@ fn handle_set_command(connection: Connection, command: &SetCommands) -> Result<(
match command { match command {
SetCommands::Rtc => { SetCommands::Rtc => {
let datetime = Local::now(); let datetime = Local::now().naive_local();
sc64.set_datetime(datetime)?; sc64.set_datetime(datetime)?;
println!( println!(
"SC64 RTC datetime synchronized to: {}", "SC64 RTC datetime synchronized to: {}",

View File

@ -25,7 +25,7 @@ use self::{
get_config, get_setting, Config, ConfigId, FirmwareStatus, Setting, SettingId, UpdateStatus, get_config, get_setting, Config, ConfigId, FirmwareStatus, Setting, SettingId, UpdateStatus,
}, },
}; };
use chrono::{DateTime, Local}; use chrono::NaiveDateTime;
use rand::Rng; use rand::Rng;
use std::{ use std::{
cmp::min, cmp::min,
@ -55,7 +55,7 @@ pub struct DeviceState {
pub button_mode: ButtonMode, pub button_mode: ButtonMode,
pub rom_extended_enable: Switch, pub rom_extended_enable: Switch,
pub led_enable: Switch, pub led_enable: Switch,
pub datetime: DateTime<Local>, pub datetime: NaiveDateTime,
pub fpga_debug_data: FpgaDebugData, pub fpga_debug_data: FpgaDebugData,
pub diagnostic_data: DiagnosticData, pub diagnostic_data: DiagnosticData,
} }
@ -210,7 +210,7 @@ impl SC64 {
Ok(()) Ok(())
} }
fn command_time_get(&mut self) -> Result<DateTime<Local>, Error> { fn command_time_get(&mut self) -> Result<NaiveDateTime, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(&Command {
id: b't', id: b't',
args: [0, 0], args: [0, 0],
@ -224,7 +224,7 @@ impl SC64 {
Ok(convert_to_datetime(&data[0..8].try_into().unwrap())?) Ok(convert_to_datetime(&data[0..8].try_into().unwrap())?)
} }
fn command_time_set(&mut self, datetime: DateTime<Local>) -> Result<(), Error> { fn command_time_set(&mut self, datetime: NaiveDateTime) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(&Command {
id: b'T', id: b'T',
args: convert_from_datetime(datetime), args: convert_from_datetime(datetime),
@ -529,11 +529,11 @@ impl SC64 {
self.command_config_set(Config::TvType(tv_type)) self.command_config_set(Config::TvType(tv_type))
} }
pub fn get_datetime(&mut self) -> Result<DateTime<Local>, Error> { pub fn get_datetime(&mut self) -> Result<NaiveDateTime, Error> {
self.command_time_get() self.command_time_get()
} }
pub fn set_datetime(&mut self, datetime: DateTime<Local>) -> Result<(), Error> { pub fn set_datetime(&mut self, datetime: NaiveDateTime) -> Result<(), Error> {
self.command_time_set(datetime) self.command_time_set(datetime)
} }

View File

@ -1,5 +1,5 @@
use super::Error; use super::Error;
use chrono::{DateTime, Datelike, Local, NaiveDateTime, TimeZone, Timelike}; use chrono::{Datelike, NaiveDateTime, Timelike};
pub fn u8_from_bcd(value: u8) -> u8 { pub fn u8_from_bcd(value: u8) -> u8 {
(((value & 0xF0) >> 4) * 10) + (value & 0x0F) (((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) (((value / 10) & 0x0F) << 4) | ((value % 10) & 0x0F)
} }
pub fn convert_to_datetime(data: &[u8; 8]) -> Result<DateTime<Local>, Error> { pub fn convert_to_datetime(data: &[u8; 8]) -> Result<NaiveDateTime, Error> {
let hour = u8_from_bcd(data[1]);
let minute = u8_from_bcd(data[2]);
let second = u8_from_bcd(data[3]); let second = u8_from_bcd(data[3]);
let year = u8_from_bcd(data[5]) as u32 + 2000; let minute = u8_from_bcd(data[2]);
let month = u8_from_bcd(data[6]); let hour = u8_from_bcd(data[1]);
let day = u8_from_bcd(data[7]); let day = u8_from_bcd(data[7]);
let native = &NaiveDateTime::parse_from_str( let month = u8_from_bcd(data[6]);
&format!("{year:02}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"), 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", "%Y-%m-%dT%H:%M:%S",
) )
.map_err(|_| Error::new("Couldn't convert from bytes to DateTime<Local>"))?; .map_err(|_| Error::new("Couldn't convert from bytes to NaiveDateTime"))
Ok(Local.from_local_datetime(native).unwrap())
} }
pub fn convert_from_datetime(datetime: DateTime<Local>) -> [u32; 2] { pub fn convert_from_datetime(datetime: NaiveDateTime) -> [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);
let second = bcd_from_u8(datetime.second() as u8); let second = bcd_from_u8(datetime.second() as u8);
let year = bcd_from_u8((datetime.year() - 2000) as u8); let minute = bcd_from_u8(datetime.minute() as u8);
let month = bcd_from_u8(datetime.month() 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 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([weekday, hour, minute, second]),
u32::from_be_bytes([0, year, month, day]), u32::from_be_bytes([century, year, month, day]),
] ]
} }