blob: 73e80a7063e281cb7853f6fdc13d77da8ab7422c [file] [log] [blame]
Jon Flatley48f64de2022-01-06 09:53:40 -05001// Copyright lowRISC contributors.
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{bail, Result};
6use serde::Deserialize;
7use std::fs;
8use std::path::Path;
9
10/// SECDED matrix used for ECC in OTP.
11#[derive(Deserialize, Debug)]
12pub 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)]
23pub struct LcState {
24 secded: LcSecded,
25}
26
Jon Flatley3a344e92022-08-18 16:30:54 -040027#[repr(u32)]
28pub enum LcStateVal {
29 Test = 0xb2865fbb,
30 Dev = 0x0b5a75e0,
31 Prod = 0x65f2520f,
32 ProdEnd = 0x91b9b68a,
33 Rma = 0xcf8cfaab,
34}
35
Jon Flatley48f64de2022-01-06 09:53:40 -050036impl 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)]
83mod tests {
84 use super::*;
Jon Flatley20f7f992022-02-02 14:24:18 -050085 use crate::testdata;
Jon Flatley48f64de2022-01-06 09:53:40 -050086 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 Flatley20f7f992022-02-02 14:24:18 -050092 let _: LcState = from_str(&read_to_string(&testdata!("lc_ctrl_state.hjson"))?)?;
Jon Flatley48f64de2022-01-06 09:53:40 -050093 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}