p4rs/
bitmath.rs

1// Copyright 2022 Oxide Computer Company
2
3use bitvec::prelude::*;
4
5pub fn add_be(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
6    let len = usize::max(a.len(), b.len());
7
8    // P4 spec says width limits are architecture defined, i here by define
9    // softnpu to have an architectural bit-type width limit of 128.
10    let x: u128 = a.load_be();
11    let y: u128 = b.load_be();
12    let z = x + y;
13    let mut c = BitVec::new();
14    c.resize(len, false);
15    c.store_be(z);
16    c
17}
18
19pub fn add_le(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
20    let len = usize::max(a.len(), b.len());
21
22    // P4 spec says width limits are architecture defined, i here by define
23    // softnpu to have an architectural bit-type width limit of 128.
24    let x: u128 = a.load_le();
25    let y: u128 = b.load_le();
26    let z = x + y;
27    let mut c = BitVec::new();
28    c.resize(len, false);
29    c.store_le(z);
30    c
31}
32
33// leaving here in case we have a need for a true arbitrary-width adder.
34#[allow(dead_code)]
35pub fn add_generic(
36    a: BitVec<u8, Msb0>,
37    b: BitVec<u8, Msb0>,
38) -> BitVec<u8, Msb0> {
39    if a.len() != b.len() {
40        panic!("bitvec add size mismatch");
41    }
42    let mut c = BitVec::new();
43    c.resize(a.len(), false);
44
45    for i in (1..a.len()).rev() {
46        let y = c[i];
47        let x = a[i] ^ b[i];
48        if !(a[i] | b[i]) {
49            continue;
50        }
51        c.set(i, x ^ y);
52        let mut z = (a[i] && b[i]) | y;
53        for j in (1..i).rev() {
54            if !z {
55                break;
56            }
57            z = c[j];
58            c.set(j, true);
59        }
60    }
61
62    c
63}
64
65pub fn mod_be(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
66    let len = usize::max(a.len(), b.len());
67
68    // P4 spec says width limits are architecture defined, i here by define
69    // softnpu to have an architectural bit-type width limit of 128.
70    let x: u128 = a.load_be();
71    let y: u128 = b.load_be();
72    let z = x % y;
73    let mut c = BitVec::new();
74    c.resize(len, false);
75    c.store_be(z);
76    c
77}
78
79pub fn mod_le(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
80    let len = usize::max(a.len(), b.len());
81
82    // P4 spec says width limits are architecture defined, i here by define
83    // softnpu to have an architectural bit-type width limit of 128.
84    let x: u128 = a.load_le();
85    let y: u128 = b.load_le();
86    let z = x % y;
87    let mut c = BitVec::new();
88    c.resize(len, false);
89    c.store_le(z);
90    c
91}
92
93#[cfg(test)]
94mod tests {
95
96    #[test]
97    fn bitmath_add() {
98        use super::*;
99        let mut a = bitvec![mut u8, Msb0; 0; 16];
100        a.store_be(47);
101        let mut b = bitvec![mut u8, Msb0; 0; 16];
102        b.store_be(74);
103
104        println!("{:?}", a);
105        println!("{:?}", b);
106        let c = add_be(a, b);
107        println!("{:?}", c);
108
109        let cc: u128 = c.load_be();
110        assert_eq!(cc, 47u128 + 74u128);
111    }
112
113    #[test]
114    fn bitmath_add_mixed_size() {
115        use super::*;
116        let mut a = bitvec![mut u8, Msb0; 0; 8];
117        a.store_be(0xAB);
118        let mut b = bitvec![mut u8, Msb0; 0; 16];
119        b.store_be(0xCDE);
120
121        println!("{:?}", a);
122        println!("{:?}", b);
123        let c = add_be(a, b);
124        println!("{:?}", c);
125
126        let cc: u128 = c.load_be();
127        assert_eq!(cc, 0xABu128 + 0xCDEu128);
128    }
129
130    #[test]
131    fn bitmath_add_cascade() {
132        use super::*;
133        let mut a = bitvec![mut u8, Msb0; 0; 16];
134        a.store_be(47);
135        let mut b = bitvec![mut u8, Msb0; 0; 16];
136        b.store_be(74);
137        let mut c = bitvec![mut u8, Msb0; 0; 16];
138        c.store_be(123);
139        let mut d = bitvec![mut u8, Msb0; 0; 16];
140        d.store_be(9876);
141
142        let e = add_be(a, add_be(b, add_be(c, d)));
143
144        let ee: u128 = e.load_be();
145        assert_eq!(ee, 47u128 + 74u128 + 123u128 + 9876u128);
146    }
147
148    #[test]
149    fn bitmath_add_nest() {
150        use super::*;
151        let mut orig_l3_len = bitvec![mut u8, Msb0; 0; 16usize];
152        orig_l3_len.store_le(0xe9u128);
153        let x = add_le(
154            {
155                let mut x = bitvec![mut u8, Msb0; 0; 16usize];
156                x.store_le(14u128);
157                x
158            },
159            add_le(
160                orig_l3_len.clone(),
161                add_le(
162                    {
163                        let mut x = bitvec![mut u8, Msb0; 0; 16usize];
164                        x.store_le(8u128);
165                        x
166                    },
167                    {
168                        let mut x = bitvec![mut u8, Msb0; 0; 16usize];
169                        x.store_le(8u128);
170                        x
171                    },
172                ),
173            ),
174        );
175
176        let y: u128 = x.load_le();
177        assert_eq!(y, 0xe9 + 14 + 8 + 8);
178    }
179
180    #[test]
181    fn bitmath_mod() {
182        use super::*;
183        let mut a = bitvec![mut u8, Msb0; 0; 16];
184        a.store_be(47);
185        let mut b = bitvec![mut u8, Msb0; 0; 16];
186        b.store_be(7);
187
188        println!("{:?}", a);
189        println!("{:?}", b);
190        let c = mod_be(a, b);
191        println!("{:?}", c);
192
193        let cc: u128 = c.load_be();
194        assert_eq!(cc, 47u128 % 7u128);
195    }
196}