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.wrapping_add(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.wrapping_add(y);
27    let mut c = BitVec::new();
28    c.resize(len, false);
29    c.store_le(z);
30    c
31}
32
33pub fn sub_be(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
34    let len = usize::max(a.len(), b.len());
35    let x: u128 = a.load_be();
36    let y: u128 = b.load_be();
37    let z = x.wrapping_sub(y);
38    let mut c = BitVec::new();
39    c.resize(len, false);
40    c.store_be(z);
41    c
42}
43
44pub fn sub_le(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
45    let len = usize::max(a.len(), b.len());
46    let x: u128 = a.load_le();
47    let y: u128 = b.load_le();
48    let z = x.wrapping_sub(y);
49    let mut c = BitVec::new();
50    c.resize(len, false);
51    c.store_le(z);
52    c
53}
54
55// leaving here in case we have a need for a true arbitrary-width adder.
56#[allow(dead_code)]
57pub fn add_generic(
58    a: BitVec<u8, Msb0>,
59    b: BitVec<u8, Msb0>,
60) -> BitVec<u8, Msb0> {
61    if a.len() != b.len() {
62        panic!("bitvec add size mismatch");
63    }
64    let mut c = BitVec::new();
65    c.resize(a.len(), false);
66
67    for i in (1..a.len()).rev() {
68        let y = c[i];
69        let x = a[i] ^ b[i];
70        if !(a[i] | b[i]) {
71            continue;
72        }
73        c.set(i, x ^ y);
74        let mut z = (a[i] && b[i]) | y;
75        for j in (1..i).rev() {
76            if !z {
77                break;
78            }
79            z = c[j];
80            c.set(j, true);
81        }
82    }
83
84    c
85}
86
87pub fn mod_be(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
88    let len = usize::max(a.len(), b.len());
89
90    // P4 spec says width limits are architecture defined, i here by define
91    // softnpu to have an architectural bit-type width limit of 128.
92    let x: u128 = a.load_be();
93    let y: u128 = b.load_be();
94    let z = x % y;
95    let mut c = BitVec::new();
96    c.resize(len, false);
97    c.store_be(z);
98    c
99}
100
101pub fn mod_le(a: BitVec<u8, Msb0>, b: BitVec<u8, Msb0>) -> BitVec<u8, Msb0> {
102    let len = usize::max(a.len(), b.len());
103
104    // P4 spec says width limits are architecture defined, i here by define
105    // softnpu to have an architectural bit-type width limit of 128.
106    let x: u128 = a.load_le();
107    let y: u128 = b.load_le();
108    let z = x % y;
109    let mut c = BitVec::new();
110    c.resize(len, false);
111    c.store_le(z);
112    c
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn bitmath_add_be() {
121        let mut a = bitvec![mut u8, Msb0; 0; 16];
122        a.store_be(47);
123        let mut b = bitvec![mut u8, Msb0; 0; 16];
124        b.store_be(74);
125
126        println!("{:?}", a);
127        println!("{:?}", b);
128        let c = add_be(a, b);
129        println!("{:?}", c);
130
131        let cc: u128 = c.load_be();
132        assert_eq!(cc, 47u128 + 74u128);
133    }
134
135    #[test]
136    fn bitmath_add_le() {
137        let mut a = bitvec![mut u8, Msb0; 0; 16];
138        a.store_le(47);
139        let mut b = bitvec![mut u8, Msb0; 0; 16];
140        b.store_le(74);
141
142        println!("{:?}", a);
143        println!("{:?}", b);
144        let c = add_le(a, b);
145        println!("{:?}", c);
146
147        let cc: u128 = c.load_le();
148        assert_eq!(cc, 47u128 + 74u128);
149    }
150
151    #[test]
152    fn bitmath_sub_be() {
153        use super::*;
154        let mut a = bitvec![mut u8, Msb0; 0; 16];
155        a.store_be(74);
156        let mut b = bitvec![mut u8, Msb0; 0; 16];
157        b.store_be(47);
158
159        println!("{:?}", a);
160        println!("{:?}", b);
161        let c = sub_be(a, b);
162        println!("{:?}", c);
163
164        let cc: u128 = c.load_be();
165        assert_eq!(cc, 74u128 - 47u128);
166    }
167
168    #[test]
169    fn bitmath_sub_le() {
170        use super::*;
171        let mut a = bitvec![mut u8, Msb0; 0; 16];
172        a.store_le(74);
173        let mut b = bitvec![mut u8, Msb0; 0; 16];
174        b.store_le(47);
175
176        println!("{:?}", a);
177        println!("{:?}", b);
178        let c = sub_le(a, b);
179        println!("{:?}", c);
180
181        let cc: u128 = c.load_le();
182        assert_eq!(cc, 74u128 - 47u128);
183    }
184
185    #[test]
186    fn bitmath_add_mixed_size() {
187        use super::*;
188        let mut a = bitvec![mut u8, Msb0; 0; 8];
189        a.store_be(0xAB);
190        let mut b = bitvec![mut u8, Msb0; 0; 16];
191        b.store_be(0xCDE);
192
193        println!("{:?}", a);
194        println!("{:?}", b);
195        let c = add_be(a, b);
196        println!("{:?}", c);
197
198        let cc: u128 = c.load_be();
199        assert_eq!(cc, 0xABu128 + 0xCDEu128);
200    }
201
202    #[test]
203    fn bitmath_add_cascade() {
204        use super::*;
205        let mut a = bitvec![mut u8, Msb0; 0; 16];
206        a.store_be(47);
207        let mut b = bitvec![mut u8, Msb0; 0; 16];
208        b.store_be(74);
209        let mut c = bitvec![mut u8, Msb0; 0; 16];
210        c.store_be(123);
211        let mut d = bitvec![mut u8, Msb0; 0; 16];
212        d.store_be(9876);
213
214        let e = add_be(a, add_be(b, add_be(c, d)));
215
216        let ee: u128 = e.load_be();
217        assert_eq!(ee, 47u128 + 74u128 + 123u128 + 9876u128);
218    }
219
220    #[test]
221    fn bitmath_add_nest() {
222        use super::*;
223        let mut orig_l3_len = bitvec![mut u8, Msb0; 0; 16usize];
224        orig_l3_len.store_le(0xe9u128);
225        let x = add_le(
226            {
227                let mut x = bitvec![mut u8, Msb0; 0; 16usize];
228                x.store_le(14u128);
229                x
230            },
231            add_le(
232                orig_l3_len.clone(),
233                add_le(
234                    {
235                        let mut x = bitvec![mut u8, Msb0; 0; 16usize];
236                        x.store_le(8u128);
237                        x
238                    },
239                    {
240                        let mut x = bitvec![mut u8, Msb0; 0; 16usize];
241                        x.store_le(8u128);
242                        x
243                    },
244                ),
245            ),
246        );
247
248        let y: u128 = x.load_le();
249        assert_eq!(y, 0xe9 + 14 + 8 + 8);
250    }
251
252    #[test]
253    fn bitmath_mod() {
254        use super::*;
255        let mut a = bitvec![mut u8, Msb0; 0; 16];
256        a.store_be(47);
257        let mut b = bitvec![mut u8, Msb0; 0; 16];
258        b.store_be(7);
259
260        println!("{:?}", a);
261        println!("{:?}", b);
262        let c = mod_be(a, b);
263        println!("{:?}", c);
264
265        let cc: u128 = c.load_be();
266        assert_eq!(cc, 47u128 % 7u128);
267    }
268}