use serde::{Serialize, Deserialize};
use crate::field_spec::{FieldSpec, FieldPos};
use crate::peripheral_spec::PeripheralSpec;
use crate::endian::Endian;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RegisterSpec {
pub name: String,
pub address: u64,
pub size: u8,
pub readable: bool,
pub writable: bool,
pub reset_val: Option<u64>,
pub fields: Vec<FieldSpec>,
pub access_proc: Option<String>,
}
impl RegisterSpec {
pub fn reg_mod_name(&self) -> String {
stringcase::snake_case(&self.name)
}
pub fn reg_method_name(&self) -> String {
stringcase::snake_case(&self.name)
}
pub fn reg_struct_name(&self) -> String {
stringcase::pascal_case(&self.name)
}
pub fn regval_struct_name(&self) -> String {
format!("{}Val", stringcase::pascal_case(&self.name))
}
pub fn regval_word_size(&self) -> u8 {
let len = self.size;
if len <= 2 {
len as u8
} else if len <= 4 {
4
} else if len <= 8 {
8
} else {
panic!("Invalid word size")
}
}
pub fn regval_word_name(&self) -> &'static str {
match self.regval_word_size() {
1 => "u8",
2 => "u16",
4 => "u32",
8 => "u64",
_ => panic!("Invalid word size from regval_word_size"),
}
}
pub fn commsbuf_subscript(&self, endian: Endian) -> String {
let word_size = self.regval_word_size();
let padding_len = word_size - self.size;
let (low, high) = if padding_len == 0 {
return "".to_string()
} else if matches!(endian, Endian::Big) {
(padding_len, word_size)
} else {
(0, self.size)
};
format!("[{low}..{high}]")
}
pub fn access_proc_enum(&self) -> String {
let Some(ref _proc) = self.access_proc else {
return "crate::AccessProc::Standard".to_string()
};
todo!()
}
pub fn generate_file(&self, pspec: &PeripheralSpec) -> String {
let mut out = String::new();
out.push_str(&format!("use regcomms::{{RegCommsError, RegComms}};\n"));
out.push_str(&format!("use crate::{};\n", pspec.peripheral_struct_name()));
out.push_str(&format!("pub struct {}<'a, C: RegComms{}>(pub &'a mut {}<C>);\n", self.reg_struct_name(), pspec.regcomms_params(), pspec.peripheral_struct_name()));
out.push_str(&format!("impl<'a, C: RegComms{}> {}<'a, C> {{\n", pspec.regcomms_params(), self.reg_struct_name()));
let endian = pspec.endian();
if self.readable {
out.push_str(&format!(" pub fn read(&mut self) -> Result<{}, RegCommsError> {{\n", self.regval_struct_name()));
out.push_str(&format!(" let mut buf = [0u8; {}];\n", self.regval_word_size()));
out.push_str(&format!(" self.0.comms_read(0x{:x}, &mut buf{}, {})?;\n", self.address, self.commsbuf_subscript(endian), self.access_proc_enum()));
out.push_str(&format!(" let val = {}::from_{}_bytes(buf);\n", self.regval_word_name(), endian.abbrev()));
out.push_str(&format!(" Ok({}(val))\n", self.regval_struct_name()));
out.push_str(&format!(" }}\n"));
out.push_str(&format!(" pub async fn read_async(&mut self) -> Result<{}, RegCommsError> {{\n", self.regval_struct_name()));
out.push_str(&format!(" let mut buf = [0u8; {}];\n", self.regval_word_size()));
out.push_str(&format!(" self.0.comms_read_async(0x{:x}, &mut buf{}, {}).await?;\n", self.address, self.commsbuf_subscript(endian), self.access_proc_enum()));
out.push_str(&format!(" let val = {}::from_{}_bytes(buf);\n", self.regval_word_name(), endian.abbrev()));
out.push_str(&format!(" Ok({}(val))\n", self.regval_struct_name()));
out.push_str(&format!(" }}\n"));
}
if self.writable {
out.push_str(&format!(" pub fn write(&mut self, val: {}) -> Result<(), RegCommsError> {{\n", self.regval_struct_name()));
out.push_str(&format!(" let buf = val.0.to_be_bytes();\n"));
out.push_str(&format!(" self.0.comms_write(0x{:x}, &buf{}, {})?;\n", self.address, self.commsbuf_subscript(endian), self.access_proc_enum()));
out.push_str(&format!(" Ok(())\n"));
out.push_str(&format!(" }}\n"));
out.push_str(&format!(" pub async fn write_async(&mut self, val: {}) -> Result<(), RegCommsError> {{\n", self.regval_struct_name()));
out.push_str(&format!(" let buf = val.0.to_be_bytes();\n"));
out.push_str(&format!(" self.0.comms_write_async(0x{:x}, &buf{}, {}).await?;\n", self.address, self.commsbuf_subscript(endian), self.access_proc_enum()));
out.push_str(&format!(" Ok(())\n"));
out.push_str(&format!(" }}\n"));
}
out.push_str(&format!("}}\n"));
out.push_str(&format!("pub struct {}(pub {});\n", self.regval_struct_name(), self.regval_word_name()));
out.push_str(&format!("impl {} {{\n", self.regval_struct_name()));
out.push_str(&format!(" pub fn get(&self) -> {} {{\n", self.regval_word_name()));
out.push_str(&format!(" self.0\n"));
out.push_str(&format!(" }}\n"));
if self.writable {
out.push_str(&format!(" pub fn zero() -> Self {{\n"));
out.push_str(&format!(" Self(0)\n"));
out.push_str(&format!(" }}\n"));
}
for field in self.fields.iter() {
out.push_str(&format!(" pub fn {}<'a>(&'a mut self) -> {}<'a> {{\n", field.method_name(), field.struct_name()));
out.push_str(&format!(" {}(self)\n", field.struct_name()));
out.push_str(&format!(" }}\n"));
}
out.push_str(&format!("}}\n"));
for field in self.fields.iter() {
out.push_str(&format!("pub struct {}<'a>(pub &'a mut {});\n", field.struct_name(), self.regval_struct_name()));
out.push_str(&format!("impl<'a> {}<'a> {{\n", field.struct_name()));
match field.field_pos {
FieldPos::Bit(bit_pos) => {
if self.readable {
out.push_str(&format!(" pub fn bit(&self) -> bool {{\n"));
out.push_str(&format!(" ((self.0.0 >> {}) & 1) != 0\n", bit_pos));
out.push_str(&format!(" }}\n"));
out.push_str(&format!(" pub fn bit_is_set(&self) -> bool {{\n"));
out.push_str(&format!(" self.bit()\n"));
out.push_str(&format!(" }}\n"));
}
if self.writable {
out.push_str(&format!(" pub fn assign(self, val: bool) -> &'a mut {} {{\n", self.regval_struct_name()));
out.push_str(&format!(" self.0.0 &= !(1 << {});\n", bit_pos));
out.push_str(&format!(" self.0.0 |= !(!(val as {}) << {});\n", self.regval_word_name(), bit_pos));
out.push_str(&format!(" self.0\n"));
out.push_str(&format!(" }}\n"));
out.push_str(&format!(" pub fn set_bit(self) -> &'a mut {} {{\n", self.regval_struct_name()));
out.push_str(&format!(" self.assign(true)\n"));
out.push_str(&format!(" }}\n"));
out.push_str(&format!(" pub fn clear_bit(self) -> &'a mut {} {{\n", self.regval_struct_name()));
out.push_str(&format!(" self.assign(false)\n"));
out.push_str(&format!(" }}\n"));
}
}
FieldPos::Field(high, low) => {
let field_len = high - low + 1;
if self.readable {
out.push_str(&format!(" pub fn bits(&self) -> {} {{\n", field.field_pos.fieldpos_word()));
if field_len == self.regval_word_size() * 8 {
out.push_str(&format!(" self.0.0\n"));
} else {
out.push_str(&format!(" ((self.0.0 >> {}) & !(!0 << {})) as {}\n", low, field_len, field.field_pos.fieldpos_word()));
}
out.push_str(&format!(" }}\n"));
}
if self.writable {
out.push_str(&format!(" pub fn set(self, val: {}) -> &'a mut {} {{\n", field.field_pos.fieldpos_word(), self.regval_struct_name()));
if field_len == self.regval_word_size() * 8 {
out.push_str(&format!(" self.0.0 = val;\n"));
} else {
out.push_str(&format!(" self.0.0 &= !(!(!0 << {}) << {});\n", field_len, low));
out.push_str(&format!(" self.0.0 |= ((val as {}) & !(!0 << {})) << {};\n", self.regval_word_name(), field_len, low));
}
out.push_str(&format!(" self.0\n"));
out.push_str(&format!(" }}\n"));
}
}
}
out.push_str(&format!("}}\n"));
}
out
}
}