Jon Flatley | 48f64de | 2022-01-06 09:53:40 -0500 | [diff] [blame] | 1 | // Copyright lowRISC contributors. |
| 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | // SPDX-License-Identifier: Apache-2.0 |
| 4 | |
| 5 | use anyhow::{bail, Result}; |
| 6 | use serde::Deserialize; |
| 7 | use std::fs; |
| 8 | use std::path::Path; |
| 9 | |
| 10 | /// SECDED matrix used for ECC in OTP. |
| 11 | #[derive(Deserialize, Debug)] |
| 12 | pub struct LcSecded { |
| 13 | /// The number of bits of data covered by ECC. |
| 14 | data_width: usize, |
| 15 | /// The number of ECC bits. |
| 16 | ecc_width: usize, |
| 17 | /// ECC matrix used for computing ECC bits. |
| 18 | ecc_matrix: Vec<Vec<u8>>, |
| 19 | } |
| 20 | |
| 21 | /// The internal representation of lc_ctrl_state, used in OTP operations. |
| 22 | #[derive(Deserialize, Debug)] |
| 23 | pub struct LcState { |
| 24 | secded: LcSecded, |
| 25 | } |
| 26 | |
Jon Flatley | 3a344e9 | 2022-08-18 16:30:54 -0400 | [diff] [blame^] | 27 | #[repr(u32)] |
| 28 | pub enum LcStateVal { |
| 29 | Test = 0xb2865fbb, |
| 30 | Dev = 0x0b5a75e0, |
| 31 | Prod = 0x65f2520f, |
| 32 | ProdEnd = 0x91b9b68a, |
| 33 | Rma = 0xcf8cfaab, |
| 34 | } |
| 35 | |
Jon Flatley | 48f64de | 2022-01-06 09:53:40 -0500 | [diff] [blame] | 36 | impl LcSecded { |
| 37 | pub fn new(in_file: &Path) -> Result<LcSecded> { |
| 38 | let json_text = fs::read_to_string(in_file)?; |
| 39 | let res: LcState = deser_hjson::from_str(&json_text)?; |
| 40 | if res.secded.ecc_matrix.len() != res.secded.ecc_width { |
| 41 | bail!("Bad ecc matrix length {}", res.secded.ecc_matrix.len()); |
| 42 | } |
| 43 | Ok(res.secded) |
| 44 | } |
| 45 | |
| 46 | fn bit_index(data: &[u8], index: usize) -> bool { |
| 47 | let byte = index / 8; |
| 48 | let bit = index % 8; |
| 49 | data[byte] & (1 << bit) != 0 |
| 50 | } |
| 51 | |
| 52 | pub fn ecc_encode(&self, mut data: Vec<u8>) -> Result<Vec<u8>> { |
| 53 | if data.len() * 8 != self.data_width { |
| 54 | bail!("Bad data length for ecc {}", data.len() * 8); |
| 55 | } |
| 56 | let data_len = data.len(); |
| 57 | data.resize(data_len + self.ecc_byte_len(), 0); |
| 58 | for (i, matrix) in self.ecc_matrix.iter().enumerate() { |
| 59 | let mut bit = false; |
| 60 | for j in matrix { |
| 61 | bit ^= Self::bit_index(&data, *j as usize); |
| 62 | } |
| 63 | if bit { |
| 64 | let byte = i / 8 + data_len; |
| 65 | let bit = i % 8; |
| 66 | data[byte] |= 1 << bit; |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | Ok(data) |
| 71 | } |
| 72 | |
| 73 | pub fn ecc_byte_len(&self) -> usize { |
| 74 | if self.ecc_width == 0 { |
| 75 | 0 |
| 76 | } else { |
| 77 | (self.ecc_width - 1) / 8 + 1 |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | #[cfg(test)] |
| 83 | mod tests { |
| 84 | use super::*; |
Jon Flatley | 20f7f99 | 2022-02-02 14:24:18 -0500 | [diff] [blame] | 85 | use crate::testdata; |
Jon Flatley | 48f64de | 2022-01-06 09:53:40 -0500 | [diff] [blame] | 86 | use anyhow::Result; |
| 87 | use deser_hjson::from_str; |
| 88 | use std::fs::read_to_string; |
| 89 | |
| 90 | #[test] |
| 91 | fn test_lc_state_deserialize() -> Result<()> { |
Jon Flatley | 20f7f99 | 2022-02-02 14:24:18 -0500 | [diff] [blame] | 92 | let _: LcState = from_str(&read_to_string(&testdata!("lc_ctrl_state.hjson"))?)?; |
Jon Flatley | 48f64de | 2022-01-06 09:53:40 -0500 | [diff] [blame] | 93 | Ok(()) |
| 94 | } |
| 95 | |
| 96 | #[test] |
| 97 | fn test_ecc_encode() { |
| 98 | let secded = LcSecded { |
| 99 | data_width: 16, |
| 100 | ecc_width: 6, |
| 101 | ecc_matrix: vec![ |
| 102 | vec![0, 1, 3, 4, 6, 8, 10, 11, 13, 15], // ECC bit 0 |
| 103 | vec![0, 2, 3, 5, 6, 9, 10, 12, 13], // ECC bit 1 |
| 104 | vec![1, 2, 3, 7, 8, 9, 10, 14, 15], // ECC bit 2 |
| 105 | vec![4, 5, 6, 7, 8, 9, 10], // ECC bit 3 |
| 106 | vec![11, 12, 13, 14, 15], // ECC bit 4 |
| 107 | vec![ |
| 108 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, |
| 109 | ], // Parity bit |
| 110 | ], |
| 111 | }; |
| 112 | |
| 113 | let zero: Vec<u8> = vec![0, 0]; |
| 114 | let a5a5: Vec<u8> = vec![0xa5, 0xa5]; |
| 115 | let fcc5: Vec<u8> = vec![0xfc, 0xc5]; |
| 116 | assert_eq!(vec![0u8, 0, 0], secded.ecc_encode(zero).unwrap()); |
| 117 | assert_eq!(vec![0xa5u8, 0xa5, 0x27], secded.ecc_encode(a5a5).unwrap()); |
| 118 | assert_eq!(vec![0x0fcu8, 0xc5, 0x06], secded.ecc_encode(fcc5).unwrap()) |
| 119 | } |
| 120 | } |