Skip to main content

rtlp_lib/
lib.rs

1use std::convert::TryFrom;
2use std::fmt::Display;
3
4#[macro_use]
5extern crate bitfield;
6
7/// Errors that can occur when parsing TLP packets
8#[derive(Debug, Clone, PartialEq)]
9pub enum TlpError {
10    /// Invalid format field value (bits don't match any known format)
11    InvalidFormat,
12    /// Invalid type field value (bits don't match any known type encoding)
13    InvalidType,
14    /// Unsupported combination of format and type
15    UnsupportedCombination,
16    /// Payload/header byte slice is too short to contain the expected fields
17    InvalidLength,
18}
19
20#[repr(u8)]
21#[derive(Debug, PartialEq, Copy, Clone)]
22pub enum TlpFmt {
23    NoDataHeader3DW     = 0b000,
24    NoDataHeader4DW     = 0b001,
25    WithDataHeader3DW   = 0b010,
26    WithDataHeader4DW   = 0b011,
27    TlpPrefix           = 0b100,
28}
29
30impl Display for TlpFmt {
31    fn fmt (&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
32        let name = match &self {
33            TlpFmt::NoDataHeader3DW => "3DW no Data Header",
34            TlpFmt::NoDataHeader4DW => "4DW no Data Header",
35            TlpFmt::WithDataHeader3DW => "3DW with Data Header",
36            TlpFmt::WithDataHeader4DW => "4DW with Data Header",
37            TlpFmt::TlpPrefix => "Tlp Prefix",
38        };
39        write!(fmt, "{}", name)
40    }
41}
42
43impl TryFrom<u32> for TlpFmt {
44    type Error = TlpError;
45
46    fn try_from(v: u32) -> Result<Self, Self::Error> {
47        match v {
48            x if x == TlpFmt::NoDataHeader3DW as u32 => Ok(TlpFmt::NoDataHeader3DW),
49            x if x == TlpFmt::NoDataHeader4DW as u32 => Ok(TlpFmt::NoDataHeader4DW),
50            x if x == TlpFmt::WithDataHeader3DW as u32 => Ok(TlpFmt::WithDataHeader3DW),
51            x if x == TlpFmt::WithDataHeader4DW as u32 => Ok(TlpFmt::WithDataHeader4DW),
52            x if x == TlpFmt::TlpPrefix as u32 => Ok(TlpFmt::TlpPrefix),
53            _ => Err(TlpError::InvalidFormat),
54        }
55    }
56}
57
58/// Atomic operation discriminant
59#[derive(Debug, Copy, Clone, PartialEq)]
60pub enum AtomicOp {
61    FetchAdd,
62    Swap,
63    CompareSwap,
64}
65
66/// Operand width — derived from TLP format: 3DW → 32-bit, 4DW → 64-bit
67#[derive(Debug, Copy, Clone, PartialEq)]
68pub enum AtomicWidth {
69    W32,
70    W64,
71}
72
73#[derive(PartialEq)]
74pub enum TlpFormatEncodingType {
75    MemoryRequest           = 0b00000,
76    MemoryLockRequest       = 0b00001,
77    IORequest               = 0b00010,
78    ConfigType0Request      = 0b00100,
79    ConfigType1Request      = 0b00101,
80    Completion              = 0b01010,
81    CompletionLocked        = 0b01011,
82    FetchAtomicOpRequest    = 0b01100,
83    UnconSwapAtomicOpRequest= 0b01101,
84    CompSwapAtomicOpRequest = 0b01110,
85    DeferrableMemoryWriteRequest = 0b11011,
86}
87
88impl TryFrom<u32> for TlpFormatEncodingType {
89    type Error = TlpError;
90
91    fn try_from(v: u32) -> Result<Self, Self::Error> {
92        match v {
93            x if x == TlpFormatEncodingType::MemoryRequest as u32 			=> Ok(TlpFormatEncodingType::MemoryRequest),
94            x if x == TlpFormatEncodingType::MemoryLockRequest as u32 		=> Ok(TlpFormatEncodingType::MemoryLockRequest),
95            x if x == TlpFormatEncodingType::IORequest as u32 				=> Ok(TlpFormatEncodingType::IORequest),
96            x if x == TlpFormatEncodingType::ConfigType0Request as u32 		=> Ok(TlpFormatEncodingType::ConfigType0Request),
97            x if x == TlpFormatEncodingType::ConfigType1Request as u32 		=> Ok(TlpFormatEncodingType::ConfigType1Request),
98            x if x == TlpFormatEncodingType::Completion as u32 				=> Ok(TlpFormatEncodingType::Completion),
99            x if x == TlpFormatEncodingType::CompletionLocked  as u32 		=> Ok(TlpFormatEncodingType::CompletionLocked),
100            x if x == TlpFormatEncodingType::FetchAtomicOpRequest as u32 	=> Ok(TlpFormatEncodingType::FetchAtomicOpRequest),
101            x if x == TlpFormatEncodingType::UnconSwapAtomicOpRequest as u32 => Ok(TlpFormatEncodingType::UnconSwapAtomicOpRequest),
102            x if x == TlpFormatEncodingType::CompSwapAtomicOpRequest as u32 => Ok(TlpFormatEncodingType::CompSwapAtomicOpRequest),
103            x if x == TlpFormatEncodingType::DeferrableMemoryWriteRequest as u32 => Ok(TlpFormatEncodingType::DeferrableMemoryWriteRequest),
104            _ => Err(TlpError::InvalidType),
105        }
106    }
107}
108
109#[derive(PartialEq)]
110#[derive(Debug)]
111pub enum TlpType {
112    MemReadReq,
113    MemReadLockReq,
114    MemWriteReq,
115    IOReadReq,
116    IOWriteReq,
117    ConfType0ReadReq,
118    ConfType0WriteReq,
119    ConfType1ReadReq,
120    ConfType1WriteReq,
121    MsgReq,
122    MsgReqData,
123    Cpl,
124    CplData,
125    CplLocked,
126    CplDataLocked,
127    FetchAddAtomicOpReq,
128    SwapAtomicOpReq,
129    CompareSwapAtomicOpReq,
130    DeferrableMemWriteReq,
131    LocalTlpPrefix,
132    EndToEndTlpPrefix,
133}
134
135impl Display for TlpType {
136    fn fmt (&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
137        let name = match &self {
138            TlpType::MemReadReq => "Memory Read Request",
139            TlpType::MemReadLockReq => "Locked Memory Read Request",
140            TlpType::MemWriteReq => "Memory Write Request",
141            TlpType::IOReadReq => "IO Read Request",
142            TlpType::IOWriteReq => "IO Write Request",
143            TlpType::ConfType0ReadReq => "Type 0 Config Read Request",
144            TlpType::ConfType0WriteReq => "Type 0 Config Write Request",
145            TlpType::ConfType1ReadReq => "Type 1 Config Read Request",
146            TlpType::ConfType1WriteReq => "Type 1 Config Write Request",
147            TlpType::MsgReq => "Message Request",
148            TlpType::MsgReqData => "Message with Data Request",
149            TlpType::Cpl => "Completion",
150            TlpType::CplData => "Completion with Data",
151            TlpType::CplLocked => "Locked Completion",
152            TlpType::CplDataLocked => "Locked Completion with Data",
153            TlpType::FetchAddAtomicOpReq => "Fetch Add Atomic Op Request",
154            TlpType::SwapAtomicOpReq => "Swap Atomic Op Request",
155            TlpType::CompareSwapAtomicOpReq => "Compare Swap Atomic Op Request",
156            TlpType::DeferrableMemWriteReq => "Deferrable Memory Write Request",
157            TlpType::LocalTlpPrefix => "Local Tlp Prefix",
158            TlpType::EndToEndTlpPrefix => "End To End Tlp Prefix",
159        };
160        write!(fmt, "{}", name)
161    }
162}
163
164impl TlpType {
165    /// Returns `true` for non-posted TLP types (requests that expect a Completion).
166    pub fn is_non_posted(&self) -> bool {
167        matches!(self,
168            TlpType::MemReadReq |
169            TlpType::MemReadLockReq |
170            TlpType::IOReadReq | TlpType::IOWriteReq |
171            TlpType::ConfType0ReadReq | TlpType::ConfType0WriteReq |
172            TlpType::ConfType1ReadReq | TlpType::ConfType1WriteReq |
173            TlpType::FetchAddAtomicOpReq | TlpType::SwapAtomicOpReq | TlpType::CompareSwapAtomicOpReq |
174            TlpType::DeferrableMemWriteReq
175        )
176    }
177}
178
179bitfield! {
180        struct TlpHeader(MSB0 [u8]);
181        u32;
182        get_format, _: 2, 0;
183        get_type,   _: 7, 3;
184        get_t9,     _: 8, 8;
185        get_tc,     _: 11, 9;
186        get_t8,     _: 12, 12;
187        get_attr_b2, _: 13, 13;
188        get_ln,     _: 14, 14;
189        get_th,     _: 15, 15;
190        get_td,     _: 16, 16;
191        get_ep,     _: 17, 17;
192        get_attr,   _: 19, 18;
193        get_at,     _: 21, 20;
194        get_length, _: 31, 22;
195}
196
197impl<T: AsRef<[u8]>> TlpHeader<T> {
198
199    fn get_tlp_type(&self) -> Result<TlpType, TlpError> {
200        let tlp_type = self.get_type();
201        let tlp_fmt = self.get_format();
202
203        match TlpFormatEncodingType::try_from(tlp_type) {
204            Ok(TlpFormatEncodingType::MemoryRequest) => {
205                match TlpFmt::try_from(tlp_fmt) {
206                    Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::MemReadReq),
207                    Ok(TlpFmt::NoDataHeader4DW) => Ok(TlpType::MemReadReq),
208                    Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::MemWriteReq),
209                    Ok(TlpFmt::WithDataHeader4DW) => Ok(TlpType::MemWriteReq),
210					Ok(_) => Err(TlpError::UnsupportedCombination),
211					Err(e) => Err(e),
212                }
213            }
214            Ok(TlpFormatEncodingType::MemoryLockRequest) => {
215                match TlpFmt::try_from(tlp_fmt) {
216                    Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::MemReadLockReq),
217                    Ok(TlpFmt::NoDataHeader4DW) => Ok(TlpType::MemReadLockReq),
218					Ok(_) => Err(TlpError::UnsupportedCombination),
219					Err(e) => Err(e),
220                }
221            }
222			Ok(TlpFormatEncodingType::IORequest) => {
223				match TlpFmt::try_from(tlp_fmt) {
224					Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::IOReadReq),
225					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::IOWriteReq),
226					Ok(_) => Err(TlpError::UnsupportedCombination),
227					Err(e) => Err(e),
228				}
229			}
230			Ok(TlpFormatEncodingType::ConfigType0Request) => {
231				match TlpFmt::try_from(tlp_fmt) {
232					Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::ConfType0ReadReq),
233					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::ConfType0WriteReq),
234					Ok(_) => Err(TlpError::UnsupportedCombination),
235					Err(e) => Err(e),
236				}
237			}
238            Ok(TlpFormatEncodingType::ConfigType1Request) => {
239                    match TlpFmt::try_from(tlp_fmt) {
240                            Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::ConfType1ReadReq),
241                            Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::ConfType1WriteReq),
242                            Ok(_) => Err(TlpError::UnsupportedCombination),
243							Err(e) => Err(e),
244                    }
245            }
246			Ok(TlpFormatEncodingType::Completion) => {
247				match TlpFmt::try_from(tlp_fmt) {
248					Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::Cpl),
249					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::CplData),
250					Ok(_) => Err(TlpError::UnsupportedCombination),
251					Err(e) => Err(e),
252				}
253			}
254			Ok(TlpFormatEncodingType::CompletionLocked) => {
255				match TlpFmt::try_from(tlp_fmt) {
256					Ok(TlpFmt::NoDataHeader3DW) => Ok(TlpType::CplLocked),
257					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::CplDataLocked),
258					Ok(_) => Err(TlpError::UnsupportedCombination),
259					Err(e) => Err(e),
260				}
261			}
262			Ok(TlpFormatEncodingType::FetchAtomicOpRequest) => {
263				match TlpFmt::try_from(tlp_fmt) {
264					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::FetchAddAtomicOpReq),
265					Ok(TlpFmt::WithDataHeader4DW) => Ok(TlpType::FetchAddAtomicOpReq),
266					Ok(_) => Err(TlpError::UnsupportedCombination),
267					Err(e) => Err(e),
268				}
269			}
270			Ok(TlpFormatEncodingType::UnconSwapAtomicOpRequest) => {
271				match TlpFmt::try_from(tlp_fmt) {
272					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::SwapAtomicOpReq),
273					Ok(TlpFmt::WithDataHeader4DW) => Ok(TlpType::SwapAtomicOpReq),
274					Ok(_) => Err(TlpError::UnsupportedCombination),
275					Err(e) => Err(e),
276				}
277			}
278			Ok(TlpFormatEncodingType::CompSwapAtomicOpRequest) => {
279				match TlpFmt::try_from(tlp_fmt) {
280					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::CompareSwapAtomicOpReq),
281					Ok(TlpFmt::WithDataHeader4DW) => Ok(TlpType::CompareSwapAtomicOpReq),
282					Ok(_) => Err(TlpError::UnsupportedCombination),
283					Err(e) => Err(e),
284				}
285			}
286			Ok(TlpFormatEncodingType::DeferrableMemoryWriteRequest) => {
287				match TlpFmt::try_from(tlp_fmt) {
288					Ok(TlpFmt::WithDataHeader3DW) => Ok(TlpType::DeferrableMemWriteReq),
289					Ok(TlpFmt::WithDataHeader4DW) => Ok(TlpType::DeferrableMemWriteReq),
290					Ok(_) => Err(TlpError::UnsupportedCombination),
291					Err(e) => Err(e),
292				}
293			}
294			Err(e) => Err(e)
295        }
296    }
297}
298
299/// Memory Request Trait:
300/// Applies to 32 and 64 bits requests as well as legacy IO-Request
301/// (Legacy IO Request has the same structure as MemRead3DW)
302/// Software using the library may want to use trait instead of bitfield structures
303/// Both 3DW (32-bit) and 4DW (64-bit) headers implement this trait
304/// 3DW header is also used for all Legacy IO Requests.
305pub trait MemRequest {
306    fn address(&self) -> u64;
307    fn req_id(&self) -> u16;
308    fn tag(&self) -> u8;
309    fn ldwbe(&self) -> u8;
310    fn fdwbe(&self) -> u8;
311}
312
313// Structure for both 3DW Memory Request as well as Legacy IO Request
314bitfield! {
315    pub struct MemRequest3DW(MSB0 [u8]);
316    u32;
317    pub get_requester_id,   _: 15, 0;
318    pub get_tag,            _: 23, 16;
319    pub get_last_dw_be,     _: 27, 24;
320    pub get_first_dw_be,    _: 31, 28;
321    pub get_address32,      _: 63, 32;
322}
323
324bitfield! {
325    pub struct MemRequest4DW(MSB0 [u8]);
326    u64;
327    pub get_requester_id,   _: 15, 0;
328    pub get_tag,            _: 23, 16;
329    pub get_last_dw_be,     _: 27, 24;
330    pub get_first_dw_be,    _: 31, 28;
331    pub get_address64,      _: 95, 32;
332}
333
334impl <T: AsRef<[u8]>> MemRequest for MemRequest3DW<T> {
335    fn address(&self) -> u64 {
336        self.get_address32().into()
337    }
338    fn req_id(&self) -> u16 {
339        self.get_requester_id() as u16
340    }
341    fn tag(&self) -> u8 {
342        self.get_tag() as u8
343    }
344    fn ldwbe(&self) -> u8 {
345        self.get_last_dw_be() as u8
346    }
347    fn fdwbe(&self) -> u8 {
348        self.get_first_dw_be() as u8
349    }
350}
351
352impl <T: AsRef<[u8]>> MemRequest for MemRequest4DW<T> {
353    fn address(&self) -> u64 {
354        self.get_address64()
355    }
356    fn req_id(&self) -> u16 {
357        self.get_requester_id() as u16
358    }
359    fn tag(&self) -> u8 {
360        self.get_tag() as u8
361    }
362    fn ldwbe(&self) -> u8 {
363        self.get_last_dw_be() as u8
364    }
365    fn fdwbe(&self) -> u8 {
366        self.get_first_dw_be() as u8
367    }
368}
369
370/// Obtain Memory Request trait from bytes in vector as dyn
371/// This is preffered way of dealing with TLP headers as exact format (32/64 bits) is not required
372///
373/// # Examples
374///
375/// ```
376/// use std::convert::TryFrom;
377///
378/// use rtlp_lib::TlpPacket;
379/// use rtlp_lib::TlpFmt;
380/// use rtlp_lib::MemRequest;
381/// use rtlp_lib::new_mem_req;
382///
383/// let bytes = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
384/// let tlp = TlpPacket::new(bytes);
385///
386/// if let Ok(tlpfmt) = tlp.get_tlp_format() {
387///     // MemRequest contain only fields specific to PCI Memory Requests
388///     let mem_req: Box<dyn MemRequest> = new_mem_req(tlp.get_data(), &tlpfmt);
389///
390///     // Address is 64 bits regardles of TLP format
391///     //println!("Memory Request Address: {:x}", mem_req.address());
392///
393///     // Format of TLP (3DW vs 4DW) is stored in the TLP header
394///     println!("This TLP size is: {}", tlpfmt);
395///     // Type LegacyIO vs MemRead vs MemWrite is stored in first DW of TLP
396///     println!("This TLP type is: {:?}", tlp.get_tlp_type());
397/// }
398/// ```
399pub fn new_mem_req(bytes: Vec<u8>, format: &TlpFmt) -> Box<dyn MemRequest> {
400    match format {
401        TlpFmt::NoDataHeader3DW => Box::new(MemRequest3DW(bytes)),
402        TlpFmt::NoDataHeader4DW => Box::new(MemRequest4DW(bytes)),
403        TlpFmt::WithDataHeader3DW => Box::new(MemRequest3DW(bytes)),
404        TlpFmt::WithDataHeader4DW => Box::new(MemRequest4DW(bytes)),
405        TlpFmt::TlpPrefix => Box::new(MemRequest3DW(bytes)),
406    }
407}
408
409/// Configuration Request Trait:
410/// Configuration Requests Headers are always same size (3DW),
411/// this trait is provided to have same API as other headers with variable size
412pub trait ConfigurationRequest {
413    fn req_id(&self) -> u16;
414    fn tag(&self) -> u8;
415    fn bus_nr(&self) -> u8;
416    fn dev_nr(&self) -> u8;
417    fn func_nr(&self) -> u8;
418    fn ext_reg_nr(&self) -> u8;
419    fn reg_nr(&self) -> u8;
420}
421
422/// Obtain Configuration Request trait from bytes in vector as dyn
423///
424/// # Examples
425///
426/// ```
427/// use std::convert::TryFrom;
428///
429/// use rtlp_lib::TlpPacket;
430/// use rtlp_lib::TlpFmt;
431/// use rtlp_lib::ConfigurationRequest;
432/// use rtlp_lib::new_conf_req;
433///
434/// let bytes = vec![0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
435/// let tlp = TlpPacket::new(bytes);
436///
437/// if let Ok(tlpfmt) = tlp.get_tlp_format() {
438///     let config_req: Box<dyn ConfigurationRequest> = new_conf_req(tlp.get_data(), &tlpfmt);
439///
440///     //println!("Configuration Request Bus: {:x}", config_req.bus_nr());
441/// }
442/// ```
443pub fn new_conf_req(bytes: Vec<u8>, _format: &TlpFmt) -> Box<dyn ConfigurationRequest> {
444	Box::new(ConfigRequest(bytes))
445}
446
447bitfield! {
448    pub struct ConfigRequest(MSB0 [u8]);
449    u32;
450    pub get_requester_id,   _: 15, 0;
451    pub get_tag,            _: 23, 16;
452    pub get_last_dw_be,     _: 27, 24;
453    pub get_first_dw_be,    _: 31, 28;
454    pub get_bus_nr,         _: 39, 32;
455    pub get_dev_nr,         _: 44, 40;
456    pub get_func_nr,        _: 47, 45;
457    pub rsvd,               _: 51, 48;
458    pub get_ext_reg_nr,     _: 55, 52;
459    pub get_register_nr,    _: 61, 56;
460    r,                      _: 63, 62;
461}
462
463impl <T: AsRef<[u8]>> ConfigurationRequest for ConfigRequest<T> {
464    fn req_id(&self) -> u16 {
465        self.get_requester_id() as u16
466    }
467    fn tag(&self) -> u8 {
468        self.get_tag() as u8
469    }
470    fn bus_nr(&self) -> u8 {
471        self.get_bus_nr() as u8
472    }
473    fn dev_nr(&self) -> u8 {
474        self.get_dev_nr() as u8
475    }
476    fn func_nr(&self) -> u8 {
477        self.get_func_nr() as u8
478    }
479    fn ext_reg_nr(&self) -> u8 {
480        self.get_ext_reg_nr() as u8
481    }
482    fn reg_nr(&self) -> u8 {
483        self.get_register_nr() as u8
484    }
485}
486
487/// Completion Request Trait
488/// Completions are always 3DW (for with data (fmt = b010) and without data (fmt = b000) )
489/// This trait is provided to have same API as other headers with variable size
490/// To obtain this trait `new_cmpl_req()` function has to be used
491/// Trait release user from dealing with bitfield structures.
492pub trait CompletionRequest {
493    fn cmpl_id(&self) -> u16;
494    fn cmpl_stat(&self) -> u8;
495    fn bcm(&self) -> u8;
496    fn byte_cnt(&self) -> u16;
497    fn req_id(&self) -> u16;
498    fn tag(&self) -> u8;
499    fn laddr(&self) -> u8;
500}
501
502bitfield! {
503    pub struct CompletionReqDW23(MSB0 [u8]);
504    u16;
505    pub get_completer_id,   _: 15, 0;
506    pub get_cmpl_stat,      _: 18, 16;
507    pub get_bcm,            _: 19, 19;
508    pub get_byte_cnt,       _: 31, 20;
509    pub get_req_id,         _: 47, 32;
510    pub get_tag,            _: 55, 48;
511    r,                      _: 56, 56;
512    pub get_laddr,          _: 63, 57;
513}
514
515impl <T: AsRef<[u8]>> CompletionRequest for CompletionReqDW23<T> {
516    fn cmpl_id(&self) -> u16 {
517        self.get_completer_id()
518    }
519    fn cmpl_stat(&self) -> u8 {
520        self.get_cmpl_stat() as u8
521    }
522    fn bcm(&self) -> u8 {
523        self.get_bcm() as u8
524    }
525    fn byte_cnt(&self) -> u16 {
526        self.get_byte_cnt()
527    }
528    fn req_id(&self) -> u16 {
529        self.get_req_id()
530    }
531    fn tag(&self) -> u8 {
532        self.get_tag() as u8
533    }
534    fn laddr(&self) -> u8 {
535        self.get_laddr() as u8
536    }
537}
538
539/// Obtain Completion Request dyn Trait:
540///
541/// # Examples
542///
543/// ```
544/// use rtlp_lib::TlpFmt;
545/// use rtlp_lib::CompletionRequest;
546/// use rtlp_lib::new_cmpl_req;
547///
548/// let bytes = vec![0x20, 0x01, 0xFF, 0xC2, 0x00, 0x00, 0x00, 0x00];
549/// // TLP Format usually comes from TlpPacket or Header here we made up one for example
550/// let tlpfmt = TlpFmt::WithDataHeader4DW;
551///
552/// let cmpl_req: Box<dyn CompletionRequest> = new_cmpl_req(bytes, &tlpfmt);
553///
554/// println!("Requester ID from Completion{}", cmpl_req.req_id());
555/// ```
556pub fn new_cmpl_req(bytes: Vec<u8>, _format: &TlpFmt) -> Box<dyn CompletionRequest> {
557	Box::new(CompletionReqDW23(bytes))
558}
559
560/// Message Request trait
561/// Provide method to access fields in DW2-4 header is handled by TlpHeader
562pub trait MessageRequest {
563    fn req_id(&self) -> u16;
564    fn tag(&self) -> u8;
565	fn msg_code(&self) -> u8;
566	/// DW3-4 vary with Message Code Field
567    fn dw3(&self) -> u32;
568    fn dw4(&self) -> u32;
569}
570
571bitfield! {
572    pub struct MessageReqDW24(MSB0 [u8]);
573    u32;
574    pub get_requester_id,   _: 15, 0;
575    pub get_tag,            _: 23, 16;
576    pub get_msg_code,       _: 31, 24;
577    pub get_dw3,            _: 63, 32;
578    pub get_dw4,            _: 95, 64;
579}
580
581impl <T: AsRef<[u8]>> MessageRequest for MessageReqDW24<T> {
582    fn req_id(&self) -> u16 {
583        self.get_requester_id() as u16
584    }
585    fn tag(&self) -> u8 {
586        self.get_tag() as u8
587    }
588    fn msg_code(&self) -> u8 {
589        self.get_msg_code() as u8
590    }
591    fn dw3(&self) -> u32 {
592        self.get_dw3()
593    }
594    fn dw4(&self) -> u32 {
595        self.get_dw4()
596    }
597    // TODO: implement routedby method based on type
598}
599
600/// Obtain Message Request dyn Trait:
601///
602/// # Examples
603///
604/// ```
605/// use rtlp_lib::TlpFmt;
606/// use rtlp_lib::MessageRequest;
607/// use rtlp_lib::new_msg_req;
608///
609/// let bytes = vec![0x20, 0x01, 0xFF, 0xC2, 0x00, 0x00, 0x00, 0x00];
610/// let tlpfmt = TlpFmt::NoDataHeader3DW;
611///
612/// let msg_req: Box<dyn MessageRequest> = new_msg_req(bytes, &tlpfmt);
613///
614/// println!("Requester ID from Message{}", msg_req.req_id());
615/// ```
616pub fn new_msg_req(bytes: Vec<u8>, _format: &TlpFmt) -> Box<dyn MessageRequest> {
617	Box::new(MessageReqDW24(bytes))
618}
619
620/// Atomic Request trait: header fields and operand(s) for atomic op TLPs.
621/// Use `new_atomic_req()` to obtain a trait object from raw packet bytes.
622pub trait AtomicRequest: std::fmt::Debug {
623    fn op(&self) -> AtomicOp;
624    fn width(&self) -> AtomicWidth;
625    fn req_id(&self) -> u16;
626    fn tag(&self) -> u8;
627    fn address(&self) -> u64;
628    /// Primary operand: addend (FetchAdd), new value (Swap), compare value (CAS)
629    fn operand0(&self) -> u64;
630    /// Second operand: swap value for CAS; `None` for FetchAdd and Swap
631    fn operand1(&self) -> Option<u64>;
632}
633
634#[derive(Debug)]
635struct AtomicReq {
636    op:       AtomicOp,
637    width:    AtomicWidth,
638    req_id:   u16,
639    tag:      u8,
640    address:  u64,
641    operand0: u64,
642    operand1: Option<u64>,
643}
644
645impl AtomicRequest for AtomicReq {
646    fn op(&self)       -> AtomicOp    { self.op }
647    fn width(&self)    -> AtomicWidth { self.width }
648    fn req_id(&self)   -> u16         { self.req_id }
649    fn tag(&self)      -> u8          { self.tag }
650    fn address(&self)  -> u64         { self.address }
651    fn operand0(&self) -> u64         { self.operand0 }
652    fn operand1(&self) -> Option<u64> { self.operand1 }
653}
654
655fn read_operand_be(b: &[u8], off: usize, width: AtomicWidth) -> u64 {
656    match width {
657        AtomicWidth::W32 => u32::from_be_bytes([b[off], b[off+1], b[off+2], b[off+3]]) as u64,
658        AtomicWidth::W64 => u64::from_be_bytes([
659            b[off], b[off+1], b[off+2], b[off+3],
660            b[off+4], b[off+5], b[off+6], b[off+7],
661        ]),
662    }
663}
664
665/// Parse an atomic TLP request from a `TlpPacket`.
666///
667/// The TLP type and format are extracted from the packet header.
668/// Returns `Err(TlpError::UnsupportedCombination)` if the packet does not
669/// encode one of the three atomic op types, and `Err(TlpError::InvalidLength)`
670/// if the data payload has the wrong size for the expected header and operands.
671///
672/// # Examples
673///
674/// ```
675/// use rtlp_lib::{TlpPacket, AtomicRequest, new_atomic_req};
676///
677/// // FetchAdd 3DW: DW0 byte0 = (fmt=0b010 << 5) | typ=0b01100 = 0x4C
678/// let bytes = vec![
679///     0x4C, 0x00, 0x00, 0x00, // DW0: WithDataHeader3DW / FetchAdd
680///     0xAB, 0xCD, 0x01, 0x00, // DW1: req_id=0xABCD tag=1 BE=0
681///     0x00, 0x00, 0x10, 0x00, // DW2: address32=0x0000_1000
682///     0x00, 0x00, 0x00, 0x04, // operand: addend=4
683/// ];
684/// let pkt = TlpPacket::new(bytes);
685/// let ar = new_atomic_req(&pkt).unwrap();
686/// assert_eq!(ar.req_id(),   0xABCD);
687/// assert_eq!(ar.operand0(), 4);
688/// assert!(ar.operand1().is_none());
689/// ```
690pub fn new_atomic_req(pkt: &TlpPacket) -> Result<Box<dyn AtomicRequest>, TlpError> {
691    let tlp_type = pkt.get_tlp_type()?;
692    let format   = pkt.get_tlp_format()?;
693    let bytes    = pkt.get_data();
694
695    let op = match tlp_type {
696        TlpType::FetchAddAtomicOpReq    => AtomicOp::FetchAdd,
697        TlpType::SwapAtomicOpReq        => AtomicOp::Swap,
698        TlpType::CompareSwapAtomicOpReq => AtomicOp::CompareSwap,
699        _                               => return Err(TlpError::UnsupportedCombination),
700    };
701    let (width, hdr_len) = match format {
702        TlpFmt::WithDataHeader3DW => (AtomicWidth::W32, 8usize),
703        TlpFmt::WithDataHeader4DW => (AtomicWidth::W64, 12usize),
704        _                         => return Err(TlpError::UnsupportedCombination),
705    };
706
707    let op_size = match width { AtomicWidth::W32 => 4usize, AtomicWidth::W64 => 8usize };
708    let num_ops = if matches!(op, AtomicOp::CompareSwap) { 2 } else { 1 };
709    let needed  = hdr_len + op_size * num_ops;
710    if bytes.len() != needed { return Err(TlpError::InvalidLength); }
711
712    let req_id  = u16::from_be_bytes([bytes[0], bytes[1]]);
713    let tag     = bytes[2];
714    let address = match width {
715        AtomicWidth::W32 => u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as u64,
716        AtomicWidth::W64 => u64::from_be_bytes([
717            bytes[4], bytes[5], bytes[6],  bytes[7],
718            bytes[8], bytes[9], bytes[10], bytes[11],
719        ]),
720    };
721
722    let operand0 = read_operand_be(&bytes, hdr_len, width);
723    let operand1 = if matches!(op, AtomicOp::CompareSwap) {
724        Some(read_operand_be(&bytes, hdr_len + op_size, width))
725    } else {
726        None
727    };
728
729    Ok(Box::new(AtomicReq { op, width, req_id, tag, address, operand0, operand1 }))
730}
731
732/// TLP Packet Header
733/// Contains bytes for Packet header and informations about TLP type
734pub struct TlpPacketHeader {
735    header: TlpHeader<Vec<u8>>,
736}
737
738impl TlpPacketHeader {
739    pub fn new(bytes: Vec<u8>) -> TlpPacketHeader {
740        let mut dw0 = vec![0; 4];
741        dw0[..4].clone_from_slice(&bytes[0..4]);
742
743        TlpPacketHeader { header: TlpHeader(dw0) }
744    }
745
746    pub fn get_tlp_type(&self) -> Result<TlpType, TlpError> {
747        self.header.get_tlp_type()
748    }
749
750    pub fn get_format(&self) -> u32 {self.header.get_format()}
751    pub fn get_type(&self) -> u32 {self.header.get_type()}
752    pub fn get_t9(&self) -> u32 {self.header.get_t9()}
753    pub fn get_tc(&self) -> u32 {self.header.get_tc()}
754    pub fn get_t8(&self) -> u32 {self.header.get_t8()}
755    pub fn get_attr_b2(&self) -> u32 {self.header.get_attr_b2()}
756    pub fn get_ln(&self) -> u32 {self.header.get_ln()}
757    pub fn get_th(&self) -> u32 {self.header.get_th()}
758    pub fn get_td(&self) -> u32 {self.header.get_td()}
759    pub fn get_ep(&self) -> u32 {self.header.get_ep()}
760    pub fn get_attr(&self) -> u32 {self.header.get_attr()}
761    pub fn get_at(&self) -> u32 {self.header.get_at()}
762    pub fn get_length(&self) -> u32 {self.header.get_length()}
763
764}
765
766/// TLP Packet structure is high level abstraction for entire TLP packet
767/// Contains Header and Data
768///
769/// # Examples
770///
771/// ```
772/// use rtlp_lib::TlpPacket;
773/// use rtlp_lib::TlpFmt;
774/// use rtlp_lib::TlpType;
775/// use rtlp_lib::new_msg_req;
776/// use rtlp_lib::new_conf_req;
777/// use rtlp_lib::new_mem_req;
778/// use rtlp_lib::new_cmpl_req;
779///
780/// // Bytes for full TLP Packet
781/// //               <------- DW1 -------->  <------- DW2 -------->  <------- DW3 -------->  <------- DW4 -------->
782/// let bytes = vec![0x00, 0x00, 0x20, 0x01, 0x04, 0x00, 0x00, 0x01, 0x20, 0x01, 0xFF, 0x00, 0xC2, 0x81, 0xFF, 0x10];
783/// let packet = TlpPacket::new(bytes);
784///
785/// let header = packet.get_header();
786/// // TLP Type tells us what is this packet
787/// let tlp_type = header.get_tlp_type().unwrap();
788/// let tlp_format = packet.get_tlp_format().unwrap();
789/// let requester_id;
790/// match (tlp_type) {
791///      TlpType::MemReadReq |
792///      TlpType::MemReadLockReq |
793///      TlpType::MemWriteReq |
794///      TlpType::DeferrableMemWriteReq |
795///      TlpType::IOReadReq |
796///      TlpType::IOWriteReq |
797///      TlpType::FetchAddAtomicOpReq |
798///      TlpType::SwapAtomicOpReq |
799///      TlpType::CompareSwapAtomicOpReq => requester_id = new_mem_req(packet.get_data(), &tlp_format).req_id(),
800///      TlpType::ConfType0ReadReq |
801///      TlpType::ConfType0WriteReq |
802///      TlpType::ConfType1ReadReq |
803///      TlpType::ConfType1WriteReq => requester_id = new_conf_req(packet.get_data(), &tlp_format).req_id(),
804///      TlpType::MsgReq |
805///      TlpType::MsgReqData => requester_id = new_msg_req(packet.get_data(), &tlp_format).req_id(),
806///      TlpType::Cpl |
807///      TlpType::CplData |
808///      TlpType::CplLocked |
809///      TlpType::CplDataLocked => requester_id = new_cmpl_req(packet.get_data(), &tlp_format).req_id(),
810///      TlpType::LocalTlpPrefix |
811///      TlpType::EndToEndTlpPrefix => println!("I need to implement TLP Type: {:?}", tlp_type),
812/// }
813/// ```
814pub struct TlpPacket {
815    header: TlpPacketHeader,
816    data: Vec<u8>,
817}
818
819impl TlpPacket {
820    pub fn new(bytes: Vec<u8>) -> TlpPacket {
821        let mut ownbytes = bytes.to_vec();
822        let mut header = vec![0; 4];
823        header.clone_from_slice(&ownbytes[0..4]);
824        let data = ownbytes.drain(4..).collect();
825        TlpPacket {
826            header: TlpPacketHeader::new(header),
827            data,
828        }
829    }
830
831    pub fn get_header(&self) -> &TlpPacketHeader {
832        &self.header
833    }
834
835    pub fn get_data(&self) -> Vec<u8> {
836        self.data.to_vec()
837    }
838
839    pub fn get_tlp_type(&self) -> Result<TlpType, TlpError> {
840        self.header.get_tlp_type()
841    }
842
843    pub fn get_tlp_format(&self) -> Result<TlpFmt, TlpError> {
844        TlpFmt::try_from(self.header.get_format())
845    }
846}
847
848#[cfg(test)]
849mod tests {
850    use super::*;
851
852    #[test]
853    fn tlp_header_type() {
854        // Empty packet is still MemREAD: FMT '000' Type '0 0000' Length 0
855        let memread = TlpHeader([0x0, 0x0, 0x0, 0x0]);
856        assert_eq!(memread.get_tlp_type().unwrap(), TlpType::MemReadReq);
857
858        // MemRead32 FMT '000' Type '0 0000'
859        let memread32 = TlpHeader([0x00, 0x00, 0x20, 0x01]);
860        assert_eq!(memread32.get_tlp_type().unwrap(), TlpType::MemReadReq);
861
862        // MemWrite32 FMT '010' Type '0 0000'
863        let memwrite32 = TlpHeader([0x40, 0x00, 0x00, 0x01]);
864        assert_eq!(memwrite32.get_tlp_type().unwrap(), TlpType::MemWriteReq);
865
866        // CPL without Data: FMT '000' Type '0 1010'
867        let cpl_no_data = TlpHeader([0x0a, 0x00, 0x10, 0x00]);
868        assert_eq!(cpl_no_data.get_tlp_type().unwrap(), TlpType::Cpl);
869
870        // CPL with Data: FMT '010' Type '0 1010'
871        let cpl_with_data = TlpHeader([0x4a, 0x00, 0x20, 0x40]);
872        assert_eq!(cpl_with_data.get_tlp_type().unwrap(), TlpType::CplData);
873
874        // MemRead 4DW: FMT: '001' Type '0 0000'
875        let memread_4dw = TlpHeader([0x20, 0x00, 0x20, 0x40]);
876        assert_eq!(memread_4dw.get_tlp_type().unwrap(), TlpType::MemReadReq);
877
878        // Config Type 0 Read request: FMT: '000' Type '0 0100'
879        let conf_t0_read = TlpHeader([0x04, 0x00, 0x00, 0x01]);
880        assert_eq!(conf_t0_read.get_tlp_type().unwrap(), TlpType::ConfType0ReadReq);
881
882        // Config Type 0 Write request: FMT: '010' Type '0 0100'
883        let conf_t0_write = TlpHeader([0x44, 0x00, 0x00, 0x01]);
884        assert_eq!(conf_t0_write.get_tlp_type().unwrap(), TlpType::ConfType0WriteReq);
885
886        // Config Type 1 Read request: FMT: '000' Type '0 0101'
887        let conf_t1_read = TlpHeader([0x05, 0x88, 0x80, 0x01]);
888        assert_eq!(conf_t1_read.get_tlp_type().unwrap(), TlpType::ConfType1ReadReq);
889
890        // Config Type 1 Write request: FMT: '010' Type '0 0101'
891        let conf_t1_write = TlpHeader([0x45, 0x88, 0x80, 0x01]);
892        assert_eq!(conf_t1_write.get_tlp_type().unwrap(), TlpType::ConfType1WriteReq);
893
894        // HeaderLog: 04000001 0000220f 01070000 af36fc70
895        // HeaderLog: 60009001 4000000f 00000280 4047605c
896        let memwrite64 = TlpHeader([0x60, 0x00, 0x90, 0x01]);
897        assert_eq!(memwrite64.get_tlp_type().unwrap(), TlpType::MemWriteReq);
898    }
899
900    #[test]
901    fn tlp_header_works_all_zeros() {
902        let bits_locations = TlpHeader([0x0, 0x0, 0x0, 0x0]);
903
904        assert_eq!(bits_locations.get_format(), 0);
905        assert_eq!(bits_locations.get_type(), 0);
906        assert_eq!(bits_locations.get_t9(), 0);
907        assert_eq!(bits_locations.get_tc(), 0);
908        assert_eq!(bits_locations.get_t8(), 0);
909        assert_eq!(bits_locations.get_attr_b2(), 0);
910        assert_eq!(bits_locations.get_ln(), 0);
911        assert_eq!(bits_locations.get_th(), 0);
912        assert_eq!(bits_locations.get_td(), 0);
913        assert_eq!(bits_locations.get_ep(), 0);
914        assert_eq!(bits_locations.get_attr(), 0);
915        assert_eq!(bits_locations.get_at(), 0);
916        assert_eq!(bits_locations.get_length(), 0);
917    }
918
919    #[test]
920    fn tlp_header_works_all_ones() {
921        let bits_locations = TlpHeader([0xff, 0xff, 0xff, 0xff]);
922
923        assert_eq!(bits_locations.get_format(), 0x7);
924        assert_eq!(bits_locations.get_type(), 0x1f);
925        assert_eq!(bits_locations.get_t9(), 0x1);
926        assert_eq!(bits_locations.get_tc(), 0x7);
927        assert_eq!(bits_locations.get_t8(), 0x1);
928        assert_eq!(bits_locations.get_attr_b2(), 0x1);
929        assert_eq!(bits_locations.get_ln(), 0x1);
930        assert_eq!(bits_locations.get_th(), 0x1);
931        assert_eq!(bits_locations.get_td(), 0x1);
932        assert_eq!(bits_locations.get_ep(), 0x1);
933        assert_eq!(bits_locations.get_attr(), 0x3);
934        assert_eq!(bits_locations.get_at(), 0x3);
935        assert_eq!(bits_locations.get_length(), 0x3ff);
936    }
937
938    #[test]
939    fn test_invalid_format_error() {
940        // Format field with invalid value (e.g., 0b101 = 5)
941        let invalid_fmt = TlpHeader([0xa0, 0x00, 0x00, 0x01]); // FMT='101' Type='00000'
942        let result = invalid_fmt.get_tlp_type();
943        assert!(result.is_err());
944        assert_eq!(result.unwrap_err(), TlpError::InvalidFormat);
945    }
946
947    #[test]
948    fn test_invalid_type_error() {
949        // Type field with invalid encoding (e.g., 0b01111 = 15)
950        let invalid_type = TlpHeader([0x0f, 0x00, 0x00, 0x01]); // FMT='000' Type='01111'
951        let result = invalid_type.get_tlp_type();
952        assert!(result.is_err());
953        assert_eq!(result.unwrap_err(), TlpError::InvalidType);
954    }
955
956    #[test]
957    fn test_unsupported_combination_error() {
958        // Valid format and type but unsupported combination
959        // IO Request with 4DW header (not valid)
960        let invalid_combo = TlpHeader([0x22, 0x00, 0x00, 0x01]); // FMT='001' Type='00010' (IO Request 4DW)
961        let result = invalid_combo.get_tlp_type();
962        assert!(result.is_err());
963        assert_eq!(result.unwrap_err(), TlpError::UnsupportedCombination);
964    }
965
966    // ── helpers ───────────────────────────────────────────────────────────────
967
968    /// Build a DW0-only TlpHeader from a 3-bit fmt and 5-bit type field.
969    /// byte0 layout (MSB0): bits[7:5] = fmt, bits[4:0] = type
970    fn dw0(fmt: u8, typ: u8) -> TlpHeader<[u8; 4]> {
971        TlpHeader([(fmt << 5) | (typ & 0x1f), 0x00, 0x00, 0x00])
972    }
973
974    /// Build a full TLP byte vector: DW0 header + arbitrary payload bytes.
975    /// DW0 bytes 1-3 are left 0 (length / TC / flags irrelevant for field tests).
976    fn mk_tlp(fmt: u8, typ: u8, rest: &[u8]) -> Vec<u8> {
977        let mut v = Vec::with_capacity(4 + rest.len());
978        v.push((fmt << 5) | (typ & 0x1f));
979        v.push(0x00); // TC, T9, T8, Attr_b2, LN, TH
980        v.push(0x00); // TD, Ep, Attr, AT
981        v.push(0x00); // Length
982        v.extend_from_slice(rest);
983        v
984    }
985
986    // ── happy path: every currently-supported (fmt, type) pair ────────────────
987
988    #[test]
989    fn header_decode_supported_pairs() {
990        const FMT_3DW_NO_DATA:   u8 = 0b000;
991        const FMT_4DW_NO_DATA:   u8 = 0b001;
992        const FMT_3DW_WITH_DATA: u8 = 0b010;
993        const FMT_4DW_WITH_DATA: u8 = 0b011;
994
995        const TY_MEM:        u8 = 0b00000;
996        const TY_MEM_LK:     u8 = 0b00001;
997        const TY_IO:         u8 = 0b00010;
998        const TY_CFG0:       u8 = 0b00100;
999        const TY_CFG1:       u8 = 0b00101;
1000        const TY_CPL:        u8 = 0b01010;
1001        const TY_CPL_LK:     u8 = 0b01011;
1002        const TY_ATOM_FETCH: u8 = 0b01100;
1003        const TY_ATOM_SWAP:  u8 = 0b01101;
1004        const TY_ATOM_CAS:   u8 = 0b01110;
1005        const TY_DMWR:       u8 = 0b11011;
1006
1007        // Memory Request: NoData → Read, WithData → Write; both 3DW and 4DW
1008        assert_eq!(dw0(FMT_3DW_NO_DATA,   TY_MEM).get_tlp_type().unwrap(), TlpType::MemReadReq);
1009        assert_eq!(dw0(FMT_4DW_NO_DATA,   TY_MEM).get_tlp_type().unwrap(), TlpType::MemReadReq);
1010        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_MEM).get_tlp_type().unwrap(), TlpType::MemWriteReq);
1011        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_MEM).get_tlp_type().unwrap(), TlpType::MemWriteReq);
1012
1013        // Memory Lock Request: NoData only (3DW and 4DW)
1014        assert_eq!(dw0(FMT_3DW_NO_DATA, TY_MEM_LK).get_tlp_type().unwrap(), TlpType::MemReadLockReq);
1015        assert_eq!(dw0(FMT_4DW_NO_DATA, TY_MEM_LK).get_tlp_type().unwrap(), TlpType::MemReadLockReq);
1016
1017        // IO Request: 3DW only; NoData → Read, WithData → Write
1018        assert_eq!(dw0(FMT_3DW_NO_DATA,   TY_IO).get_tlp_type().unwrap(), TlpType::IOReadReq);
1019        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_IO).get_tlp_type().unwrap(), TlpType::IOWriteReq);
1020
1021        // Config Type 0: 3DW only
1022        assert_eq!(dw0(FMT_3DW_NO_DATA,   TY_CFG0).get_tlp_type().unwrap(), TlpType::ConfType0ReadReq);
1023        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_CFG0).get_tlp_type().unwrap(), TlpType::ConfType0WriteReq);
1024
1025        // Config Type 1: 3DW only
1026        assert_eq!(dw0(FMT_3DW_NO_DATA,   TY_CFG1).get_tlp_type().unwrap(), TlpType::ConfType1ReadReq);
1027        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_CFG1).get_tlp_type().unwrap(), TlpType::ConfType1WriteReq);
1028
1029        // Completion: 3DW only; NoData → Cpl, WithData → CplData
1030        assert_eq!(dw0(FMT_3DW_NO_DATA,   TY_CPL).get_tlp_type().unwrap(), TlpType::Cpl);
1031        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_CPL).get_tlp_type().unwrap(), TlpType::CplData);
1032
1033        // Completion Locked: 3DW only
1034        assert_eq!(dw0(FMT_3DW_NO_DATA,   TY_CPL_LK).get_tlp_type().unwrap(), TlpType::CplLocked);
1035        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_CPL_LK).get_tlp_type().unwrap(), TlpType::CplDataLocked);
1036
1037        // Atomics: WithData only (3DW and 4DW)
1038        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_ATOM_FETCH).get_tlp_type().unwrap(), TlpType::FetchAddAtomicOpReq);
1039        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_ATOM_FETCH).get_tlp_type().unwrap(), TlpType::FetchAddAtomicOpReq);
1040
1041        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_ATOM_SWAP).get_tlp_type().unwrap(), TlpType::SwapAtomicOpReq);
1042        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_ATOM_SWAP).get_tlp_type().unwrap(), TlpType::SwapAtomicOpReq);
1043
1044        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_ATOM_CAS).get_tlp_type().unwrap(), TlpType::CompareSwapAtomicOpReq);
1045        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_ATOM_CAS).get_tlp_type().unwrap(), TlpType::CompareSwapAtomicOpReq);
1046
1047        // DMWr: WithData only (3DW and 4DW)
1048        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_DMWR).get_tlp_type().unwrap(), TlpType::DeferrableMemWriteReq);
1049        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_DMWR).get_tlp_type().unwrap(), TlpType::DeferrableMemWriteReq);
1050    }
1051
1052    // ── negative path: every illegal (fmt, type) pair → UnsupportedCombination ─
1053
1054    #[test]
1055    fn header_decode_rejects_unsupported_combinations() {
1056        const FMT_3DW_NO_DATA:   u8 = 0b000;
1057        const FMT_4DW_NO_DATA:   u8 = 0b001;
1058        const FMT_3DW_WITH_DATA: u8 = 0b010;
1059        const FMT_4DW_WITH_DATA: u8 = 0b011;
1060        const FMT_PREFIX:        u8 = 0b100;
1061
1062        const TY_MEM_LK:     u8 = 0b00001;
1063        const TY_IO:         u8 = 0b00010;
1064        const TY_CFG0:       u8 = 0b00100;
1065        const TY_CFG1:       u8 = 0b00101;
1066        const TY_CPL:        u8 = 0b01010;
1067        const TY_CPL_LK:     u8 = 0b01011;
1068        const TY_ATOM_FETCH: u8 = 0b01100;
1069        const TY_ATOM_SWAP:  u8 = 0b01101;
1070        const TY_ATOM_CAS:   u8 = 0b01110;
1071        const TY_DMWR:       u8 = 0b11011;
1072
1073        // IO: 4DW variants are illegal
1074        assert_eq!(dw0(FMT_4DW_NO_DATA,   TY_IO).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1075        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_IO).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1076
1077        // Config: 4DW variants are illegal (configs are always 3DW)
1078        assert_eq!(dw0(FMT_4DW_NO_DATA,   TY_CFG0).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1079        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_CFG0).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1080        assert_eq!(dw0(FMT_4DW_NO_DATA,   TY_CFG1).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1081        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_CFG1).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1082
1083        // Completions: 4DW variants are illegal
1084        assert_eq!(dw0(FMT_4DW_NO_DATA,   TY_CPL).get_tlp_type().unwrap_err(),    TlpError::UnsupportedCombination);
1085        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_CPL).get_tlp_type().unwrap_err(),    TlpError::UnsupportedCombination);
1086        assert_eq!(dw0(FMT_4DW_NO_DATA,   TY_CPL_LK).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1087        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_CPL_LK).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1088
1089        // Atomics: NoData variants are illegal (atomics always carry data)
1090        assert_eq!(dw0(FMT_3DW_NO_DATA, TY_ATOM_FETCH).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1091        assert_eq!(dw0(FMT_4DW_NO_DATA, TY_ATOM_FETCH).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1092        assert_eq!(dw0(FMT_3DW_NO_DATA, TY_ATOM_SWAP).get_tlp_type().unwrap_err(),  TlpError::UnsupportedCombination);
1093        assert_eq!(dw0(FMT_4DW_NO_DATA, TY_ATOM_SWAP).get_tlp_type().unwrap_err(),  TlpError::UnsupportedCombination);
1094        assert_eq!(dw0(FMT_3DW_NO_DATA, TY_ATOM_CAS).get_tlp_type().unwrap_err(),   TlpError::UnsupportedCombination);
1095        assert_eq!(dw0(FMT_4DW_NO_DATA, TY_ATOM_CAS).get_tlp_type().unwrap_err(),   TlpError::UnsupportedCombination);
1096
1097        // MemReadLock: WithData variants are illegal (lock is a read-only operation)
1098        assert_eq!(dw0(FMT_3DW_WITH_DATA, TY_MEM_LK).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1099        assert_eq!(dw0(FMT_4DW_WITH_DATA, TY_MEM_LK).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1100
1101        // TlpPrefix fmt (0b100) is a valid format value but illegal for all
1102        // request/completion type encodings — currently hits UnsupportedCombination
1103        assert_eq!(dw0(FMT_PREFIX, TY_IO).get_tlp_type().unwrap_err(),   TlpError::UnsupportedCombination);
1104        assert_eq!(dw0(FMT_PREFIX, TY_CPL).get_tlp_type().unwrap_err(),  TlpError::UnsupportedCombination);
1105        assert_eq!(dw0(FMT_PREFIX, TY_CFG0).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1106
1107        // DMWr: NoData variants are illegal (DMWr always carries data)
1108        assert_eq!(dw0(FMT_3DW_NO_DATA, TY_DMWR).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1109        assert_eq!(dw0(FMT_4DW_NO_DATA, TY_DMWR).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1110        assert_eq!(dw0(FMT_PREFIX,      TY_DMWR).get_tlp_type().unwrap_err(), TlpError::UnsupportedCombination);
1111    }
1112
1113    // ── DMWr: Deferrable Memory Write header decode ────────────────────────
1114
1115    #[test]
1116    fn tlp_header_dmwr32_decode() {
1117        // Fmt=010 (3DW w/ Data), Type=11011 (DMWr) → byte0 = 0x5B
1118        let dmwr32 = TlpHeader([0x5B, 0x00, 0x00, 0x00]);
1119        assert_eq!(dmwr32.get_tlp_type().unwrap(), TlpType::DeferrableMemWriteReq);
1120    }
1121
1122    #[test]
1123    fn tlp_header_dmwr64_decode() {
1124        // Fmt=011 (4DW w/ Data), Type=11011 (DMWr) → byte0 = 0x7B
1125        let dmwr64 = TlpHeader([0x7B, 0x00, 0x00, 0x00]);
1126        assert_eq!(dmwr64.get_tlp_type().unwrap(), TlpType::DeferrableMemWriteReq);
1127    }
1128
1129    #[test]
1130    fn tlp_header_dmwr_rejects_nodata_formats() {
1131        // Fmt=000, Type=11011 → byte0 = 0x1B
1132        let dmwr_bad_3dw_nodata = TlpHeader([0x1B, 0x00, 0x00, 0x00]);
1133        assert_eq!(
1134            dmwr_bad_3dw_nodata.get_tlp_type().unwrap_err(),
1135            TlpError::UnsupportedCombination
1136        );
1137
1138        // Fmt=001, Type=11011 → byte0 = 0x3B
1139        let dmwr_bad_4dw_nodata = TlpHeader([0x3B, 0x00, 0x00, 0x00]);
1140        assert_eq!(
1141            dmwr_bad_4dw_nodata.get_tlp_type().unwrap_err(),
1142            TlpError::UnsupportedCombination
1143        );
1144    }
1145
1146    #[test]
1147    fn dmwr_full_packet_3dw_fields() {
1148        // DMWr32 through TlpPacket pipeline with MemRequest3DW fields
1149        let payload = [
1150            0xAB, 0xCD, 0x42, 0x0F, // req_id=0xABCD, tag=0x42, BE=0x0F
1151            0xDE, 0xAD, 0x00, 0x00, // address32=0xDEAD0000
1152        ];
1153        let pkt = TlpPacket::new(mk_tlp(0b010, 0b11011, &payload));
1154        assert_eq!(pkt.get_tlp_type().unwrap(), TlpType::DeferrableMemWriteReq);
1155        assert_eq!(pkt.get_tlp_format().unwrap(), TlpFmt::WithDataHeader3DW);
1156
1157        let mr = new_mem_req(pkt.get_data(), &pkt.get_tlp_format().unwrap());
1158        assert_eq!(mr.req_id(), 0xABCD);
1159        assert_eq!(mr.tag(),    0x42);
1160        assert_eq!(mr.address(), 0xDEAD_0000);
1161    }
1162
1163    #[test]
1164    fn dmwr_full_packet_4dw_fields() {
1165        // DMWr64 through TlpPacket pipeline with MemRequest4DW fields
1166        let payload = [
1167            0xBE, 0xEF, 0xA5, 0x00, // req_id=0xBEEF, tag=0xA5
1168            0x11, 0x22, 0x33, 0x44, // address64 hi
1169            0x55, 0x66, 0x77, 0x88, // address64 lo
1170        ];
1171        let pkt = TlpPacket::new(mk_tlp(0b011, 0b11011, &payload));
1172        assert_eq!(pkt.get_tlp_type().unwrap(), TlpType::DeferrableMemWriteReq);
1173        assert_eq!(pkt.get_tlp_format().unwrap(), TlpFmt::WithDataHeader4DW);
1174
1175        let mr = new_mem_req(pkt.get_data(), &pkt.get_tlp_format().unwrap());
1176        assert_eq!(mr.req_id(), 0xBEEF);
1177        assert_eq!(mr.tag(),    0xA5);
1178        assert_eq!(mr.address(), 0x1122_3344_5566_7788);
1179    }
1180
1181    // ── is_non_posted() semantics ─────────────────────────────────────────────
1182
1183    #[test]
1184    fn is_non_posted_returns_true_for_non_posted_types() {
1185        assert!(TlpType::MemReadReq.is_non_posted());
1186        assert!(TlpType::MemReadLockReq.is_non_posted());
1187        assert!(TlpType::IOReadReq.is_non_posted());
1188        assert!(TlpType::IOWriteReq.is_non_posted());
1189        assert!(TlpType::ConfType0ReadReq.is_non_posted());
1190        assert!(TlpType::ConfType0WriteReq.is_non_posted());
1191        assert!(TlpType::ConfType1ReadReq.is_non_posted());
1192        assert!(TlpType::ConfType1WriteReq.is_non_posted());
1193        assert!(TlpType::FetchAddAtomicOpReq.is_non_posted());
1194        assert!(TlpType::SwapAtomicOpReq.is_non_posted());
1195        assert!(TlpType::CompareSwapAtomicOpReq.is_non_posted());
1196        assert!(TlpType::DeferrableMemWriteReq.is_non_posted());
1197    }
1198
1199    #[test]
1200    fn is_non_posted_returns_false_for_posted_types() {
1201        assert!(!TlpType::MemWriteReq.is_non_posted());
1202        assert!(!TlpType::MsgReq.is_non_posted());
1203        assert!(!TlpType::MsgReqData.is_non_posted());
1204    }
1205
1206    #[test]
1207    fn is_non_posted_returns_false_for_completions() {
1208        // Completions are responses, not requests — is_non_posted() is false
1209        assert!(!TlpType::Cpl.is_non_posted());
1210        assert!(!TlpType::CplData.is_non_posted());
1211        assert!(!TlpType::CplLocked.is_non_posted());
1212        assert!(!TlpType::CplDataLocked.is_non_posted());
1213    }
1214
1215    // ── atomic tier-A: real bytes through the full packet pipeline ─────────────
1216
1217    #[test]
1218    fn atomic_fetchadd_3dw_type_and_fields() {
1219        const FMT_3DW_WITH_DATA: u8 = 0b010;
1220        const TY_ATOM_FETCH:     u8 = 0b01100;
1221
1222        // DW1+DW2 as MemRequest3DW sees them (MSB0):
1223        //   requester_id [15:0]  = 0x1234
1224        //   tag          [23:16] = 0x56
1225        //   last_dw_be   [27:24] = 0x0  (ignored for this test)
1226        //   first_dw_be  [31:28] = 0x0  (ignored for this test)
1227        //   address32    [63:32] = 0x89ABCDEF
1228        let payload = [
1229            0x12, 0x34, // req_id
1230            0x56, 0x00, // tag, BE nibbles
1231            0x89, 0xAB, 0xCD, 0xEF, // address32
1232        ];
1233
1234        let pkt = TlpPacket::new(mk_tlp(FMT_3DW_WITH_DATA, TY_ATOM_FETCH, &payload));
1235
1236        assert_eq!(pkt.get_tlp_type().unwrap(), TlpType::FetchAddAtomicOpReq);
1237        assert_eq!(pkt.get_tlp_format().unwrap(), TlpFmt::WithDataHeader3DW);
1238
1239        let fmt = pkt.get_tlp_format().unwrap();
1240        let mr = new_mem_req(pkt.get_data(), &fmt);
1241        assert_eq!(mr.req_id(),  0x1234);
1242        assert_eq!(mr.tag(),     0x56);
1243        assert_eq!(mr.address(), 0x89AB_CDEF);
1244    }
1245
1246    #[test]
1247    fn atomic_cas_4dw_type_and_fields() {
1248        const FMT_4DW_WITH_DATA: u8 = 0b011;
1249        const TY_ATOM_CAS:       u8 = 0b01110;
1250
1251        // DW1-DW3 as MemRequest4DW sees them (MSB0):
1252        //   requester_id [15:0]  = 0xBEEF
1253        //   tag          [23:16] = 0xA5
1254        //   last/first_dw_be     = 0x00
1255        //   address64    [95:32] = 0x1122_3344_5566_7788
1256        let payload = [
1257            0xBE, 0xEF, // req_id
1258            0xA5, 0x00, // tag, BE nibbles
1259            0x11, 0x22, 0x33, 0x44, // address64 high DW
1260            0x55, 0x66, 0x77, 0x88, // address64 low DW
1261        ];
1262
1263        let pkt = TlpPacket::new(mk_tlp(FMT_4DW_WITH_DATA, TY_ATOM_CAS, &payload));
1264
1265        assert_eq!(pkt.get_tlp_type().unwrap(), TlpType::CompareSwapAtomicOpReq);
1266        assert_eq!(pkt.get_tlp_format().unwrap(), TlpFmt::WithDataHeader4DW);
1267
1268        let fmt = pkt.get_tlp_format().unwrap();
1269        let mr = new_mem_req(pkt.get_data(), &fmt);
1270        assert_eq!(mr.req_id(),  0xBEEF);
1271        assert_eq!(mr.tag(),     0xA5);
1272        assert_eq!(mr.address(), 0x1122_3344_5566_7788);
1273    }
1274
1275    // ── atomic tier-B: operand parsing via new_atomic_req() ───────────────────
1276
1277    #[test]
1278    fn fetchadd_3dw_operand() {
1279        // FetchAdd 3DW (W32): single 32-bit addend after the 8-byte header
1280        //   DW1: req_id=0xDEAD  tag=0x42  BE=0x00
1281        //   DW2: address32=0xC001_0004
1282        //   op0: addend=0x0000_000A
1283        let payload = [
1284            0xDE, 0xAD, 0x42, 0x00, // req_id, tag, BE
1285            0xC0, 0x01, 0x00, 0x04, // address32
1286            0x00, 0x00, 0x00, 0x0A, // addend (W32)
1287        ];
1288        let pkt = TlpPacket::new(mk_tlp(0b010, 0b01100, &payload));
1289        let ar  = new_atomic_req(&pkt).unwrap();
1290
1291        assert_eq!(ar.op(),       AtomicOp::FetchAdd);
1292        assert_eq!(ar.width(),    AtomicWidth::W32);
1293        assert_eq!(ar.req_id(),   0xDEAD);
1294        assert_eq!(ar.tag(),      0x42);
1295        assert_eq!(ar.address(),  0xC001_0004);
1296        assert_eq!(ar.operand0(), 0x0A);
1297        assert!(ar.operand1().is_none());
1298    }
1299
1300    #[test]
1301    fn fetchadd_4dw_operand() {
1302        // FetchAdd 4DW (W64): single 64-bit addend after the 12-byte header
1303        //   DW1: req_id=0x0042  tag=0xBB  BE=0x00
1304        //   DW2-DW3: address64=0x0000_0001_0000_0000
1305        //   op0: addend=0xFFFF_FFFF_FFFF_FFFF
1306        let payload = [
1307            0x00, 0x42, 0xBB, 0x00, // req_id, tag, BE
1308            0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // address64
1309            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // addend (W64)
1310        ];
1311        let pkt = TlpPacket::new(mk_tlp(0b011, 0b01100, &payload));
1312        let ar  = new_atomic_req(&pkt).unwrap();
1313
1314        assert_eq!(ar.op(),       AtomicOp::FetchAdd);
1315        assert_eq!(ar.width(),    AtomicWidth::W64);
1316        assert_eq!(ar.req_id(),   0x0042);
1317        assert_eq!(ar.tag(),      0xBB);
1318        assert_eq!(ar.address(),  0x0000_0001_0000_0000);
1319        assert_eq!(ar.operand0(), 0xFFFF_FFFF_FFFF_FFFF);
1320        assert!(ar.operand1().is_none());
1321    }
1322
1323    #[test]
1324    fn swap_3dw_operand() {
1325        // Swap 3DW (W32): single 32-bit swap value
1326        //   DW1: req_id=0x1111  tag=0x05  BE=0x00
1327        //   DW2: address32=0xF000_0008
1328        //   op0: new_value=0xABCD_EF01
1329        let payload = [
1330            0x11, 0x11, 0x05, 0x00, // req_id, tag, BE
1331            0xF0, 0x00, 0x00, 0x08, // address32
1332            0xAB, 0xCD, 0xEF, 0x01, // new_value (W32)
1333        ];
1334        let pkt = TlpPacket::new(mk_tlp(0b010, 0b01101, &payload));
1335        let ar  = new_atomic_req(&pkt).unwrap();
1336
1337        assert_eq!(ar.op(),       AtomicOp::Swap);
1338        assert_eq!(ar.width(),    AtomicWidth::W32);
1339        assert_eq!(ar.req_id(),   0x1111);
1340        assert_eq!(ar.tag(),      0x05);
1341        assert_eq!(ar.address(),  0xF000_0008);
1342        assert_eq!(ar.operand0(), 0xABCD_EF01);
1343        assert!(ar.operand1().is_none());
1344    }
1345
1346    #[test]
1347    fn cas_3dw_two_operands() {
1348        // CAS 3DW (W32): compare then swap — two 32-bit operands
1349        //   DW1: req_id=0xABCD  tag=0x07  BE=0x00
1350        //   DW2: address32=0x0000_4000
1351        //   op0: compare=0xCAFE_BABE
1352        //   op1: swap=0xDEAD_BEEF
1353        let payload = [
1354            0xAB, 0xCD, 0x07, 0x00, // req_id, tag, BE
1355            0x00, 0x00, 0x40, 0x00, // address32
1356            0xCA, 0xFE, 0xBA, 0xBE, // compare (W32)
1357            0xDE, 0xAD, 0xBE, 0xEF, // swap    (W32)
1358        ];
1359        let pkt = TlpPacket::new(mk_tlp(0b010, 0b01110, &payload));
1360        let ar  = new_atomic_req(&pkt).unwrap();
1361
1362        assert_eq!(ar.op(),       AtomicOp::CompareSwap);
1363        assert_eq!(ar.width(),    AtomicWidth::W32);
1364        assert_eq!(ar.req_id(),   0xABCD);
1365        assert_eq!(ar.tag(),      0x07);
1366        assert_eq!(ar.address(),  0x0000_4000);
1367        assert_eq!(ar.operand0(), 0xCAFE_BABE);
1368        assert_eq!(ar.operand1(), Some(0xDEAD_BEEF));
1369    }
1370
1371    #[test]
1372    fn cas_4dw_two_operands() {
1373        // CAS 4DW (W64): compare then swap — two 64-bit operands
1374        //   DW1: req_id=0x1234  tag=0xAA  BE=0x00
1375        //   DW2-DW3: address64=0xFFFF_FFFF_0000_0000
1376        //   op0: compare=0x0101_0101_0202_0202
1377        //   op1: swap=0x0303_0303_0404_0404
1378        let payload = [
1379            0x12, 0x34, 0xAA, 0x00, // req_id, tag, BE
1380            0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // address64
1381            0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, // compare (W64)
1382            0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, // swap    (W64)
1383        ];
1384        let pkt = TlpPacket::new(mk_tlp(0b011, 0b01110, &payload));
1385        let ar  = new_atomic_req(&pkt).unwrap();
1386
1387        assert_eq!(ar.op(),       AtomicOp::CompareSwap);
1388        assert_eq!(ar.width(),    AtomicWidth::W64);
1389        assert_eq!(ar.req_id(),   0x1234);
1390        assert_eq!(ar.tag(),      0xAA);
1391        assert_eq!(ar.address(),  0xFFFF_FFFF_0000_0000);
1392        assert_eq!(ar.operand0(), 0x0101_0101_0202_0202);
1393        assert_eq!(ar.operand1(), Some(0x0303_0303_0404_0404));
1394    }
1395
1396    #[test]
1397    fn atomic_req_rejects_wrong_tlp_type() {
1398        // MemRead type is not an atomic — should get UnsupportedCombination
1399        let pkt = TlpPacket::new(mk_tlp(0b000, 0b00000, &[0u8; 16]));
1400        assert_eq!(new_atomic_req(&pkt).err().unwrap(), TlpError::UnsupportedCombination);
1401    }
1402
1403    #[test]
1404    fn atomic_req_rejects_wrong_format() {
1405        // FetchAdd type with NoData3DW format is an invalid combo:
1406        // get_tlp_type() returns UnsupportedCombination, which propagates
1407        let pkt = TlpPacket::new(mk_tlp(0b000, 0b01100, &[0u8; 16]));
1408        assert_eq!(new_atomic_req(&pkt).err().unwrap(), TlpError::UnsupportedCombination);
1409    }
1410
1411    #[test]
1412    fn atomic_req_rejects_short_payload() {
1413        // 3 bytes data — FetchAdd 3DW needs exactly 12 (8 hdr + 4 operand)
1414        let pkt = TlpPacket::new(mk_tlp(0b010, 0b01100, &[0u8; 3]));
1415        assert_eq!(new_atomic_req(&pkt).err().unwrap(), TlpError::InvalidLength);
1416
1417        // 8 bytes data — header OK but operand missing (needs 12)
1418        let pkt = TlpPacket::new(mk_tlp(0b010, 0b01100, &[0u8; 8]));
1419        assert_eq!(new_atomic_req(&pkt).err().unwrap(), TlpError::InvalidLength);
1420
1421        // 20 bytes data — CAS 4DW needs exactly 28 (12 hdr + 8 + 8)
1422        let pkt = TlpPacket::new(mk_tlp(0b011, 0b01110, &[0u8; 20]));
1423        assert_eq!(new_atomic_req(&pkt).err().unwrap(), TlpError::InvalidLength);
1424    }
1425
1426    // ── helpers ───────────────────────────────────────────────────────────────
1427
1428    fn mk_pkt(fmt: u8, typ: u8, data: &[u8]) -> TlpPacket {
1429        TlpPacket::new(mk_tlp(fmt, typ, data))
1430    }
1431
1432    // ── atomic tier-B (new API): real binary layout, single-argument call ─────
1433
1434    #[test]
1435    fn atomic_fetchadd_3dw_32_parses_operands() {
1436        // FetchAdd 3DW (W32): 8-byte header + 4-byte addend
1437        let data = [
1438            0x01, 0x00, 0x01, 0x00, // req_id=0x0100, tag=0x01, BE=0x00
1439            0x00, 0x00, 0x10, 0x00, // address32=0x0000_1000
1440            0x00, 0x00, 0x00, 0x07, // addend=7
1441        ];
1442        let pkt = mk_pkt(0b010, 0b01100, &data);
1443        let a = new_atomic_req(&pkt).unwrap();
1444        assert_eq!(a.op(),       AtomicOp::FetchAdd);
1445        assert_eq!(a.width(),    AtomicWidth::W32);
1446        assert_eq!(a.req_id(),   0x0100);
1447        assert_eq!(a.tag(),      0x01);
1448        assert_eq!(a.address(),  0x0000_1000);
1449        assert_eq!(a.operand0(), 7);
1450        assert!(a.operand1().is_none());
1451    }
1452
1453    #[test]
1454    fn atomic_swap_4dw_64_parses_operands() {
1455        // Swap 4DW (W64): 12-byte header + 8-byte new value
1456        let data = [
1457            0xBE, 0xEF, 0xA5, 0x00, // req_id=0xBEEF, tag=0xA5, BE=0x00
1458            0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // address64=0x0000_0001_0000_0000
1459            0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, // new_value
1460        ];
1461        let pkt = mk_pkt(0b011, 0b01101, &data);
1462        let a = new_atomic_req(&pkt).unwrap();
1463        assert_eq!(a.op(),       AtomicOp::Swap);
1464        assert_eq!(a.width(),    AtomicWidth::W64);
1465        assert_eq!(a.req_id(),   0xBEEF);
1466        assert_eq!(a.tag(),      0xA5);
1467        assert_eq!(a.address(),  0x0000_0001_0000_0000);
1468        assert_eq!(a.operand0(), 0xDEAD_BEEF_CAFE_BABE);
1469        assert!(a.operand1().is_none());
1470    }
1471
1472    #[test]
1473    fn atomic_cas_3dw_32_parses_operands() {
1474        // CAS 3DW (W32): 8-byte header + 4-byte compare + 4-byte swap
1475        let data = [
1476            0xAB, 0xCD, 0x07, 0x00, // req_id=0xABCD, tag=0x07, BE=0x00
1477            0x00, 0x00, 0x40, 0x00, // address32=0x0000_4000
1478            0xCA, 0xFE, 0xBA, 0xBE, // compare
1479            0xDE, 0xAD, 0xBE, 0xEF, // swap
1480        ];
1481        let pkt = mk_pkt(0b010, 0b01110, &data);
1482        let a = new_atomic_req(&pkt).unwrap();
1483        assert_eq!(a.op(),       AtomicOp::CompareSwap);
1484        assert_eq!(a.width(),    AtomicWidth::W32);
1485        assert_eq!(a.req_id(),   0xABCD);
1486        assert_eq!(a.tag(),      0x07);
1487        assert_eq!(a.address(),  0x0000_4000);
1488        assert_eq!(a.operand0(), 0xCAFE_BABE);
1489        assert_eq!(a.operand1(), Some(0xDEAD_BEEF));
1490    }
1491
1492    // ── CompletionReqDW23: Lower Address 7-bit decode ──────────────────────
1493
1494    #[test]
1495    fn completion_laddr_full_7_bits() {
1496        // Lower Address = 0x7F (127) — all 7 bits set
1497        // DW2 byte 3: R(1 bit)=0, LowerAddr(7 bits)=0x7F → byte = 0x7F
1498        let bytes = vec![
1499            0x00, 0x00, 0x00, 0x00, // completer_id, cmpl_stat, bcm, byte_cnt
1500            0x00, 0x00, 0x00, 0x7F, // req_id, tag, R=0, laddr=0x7F
1501        ];
1502        let cmpl = new_cmpl_req(bytes, &TlpFmt::NoDataHeader3DW);
1503        assert_eq!(cmpl.laddr(), 0x7F);
1504    }
1505
1506    #[test]
1507    fn completion_laddr_bit6_set() {
1508        // Lower Address = 64 (0x40) — bit 6 is the bit that was previously lost
1509        // DW2 byte 3: R=0, LowerAddr=0x40 → byte = 0x40
1510        let bytes = vec![
1511            0x00, 0x00, 0x00, 0x00,
1512            0x00, 0x00, 0x00, 0x40,
1513        ];
1514        let cmpl = new_cmpl_req(bytes, &TlpFmt::NoDataHeader3DW);
1515        assert_eq!(cmpl.laddr(), 0x40);
1516    }
1517
1518    #[test]
1519    fn completion_laddr_with_reserved_bit_set() {
1520        // R=1, LowerAddr=0x55 (85)
1521        // DW2 byte 3: 1_1010101 = 0xD5
1522        let bytes = vec![
1523            0x00, 0x00, 0x00, 0x00,
1524            0x00, 0x00, 0x00, 0xD5,
1525        ];
1526        let cmpl = new_cmpl_req(bytes, &TlpFmt::NoDataHeader3DW);
1527        assert_eq!(cmpl.laddr(), 0x55);
1528    }
1529
1530    #[test]
1531    fn completion_full_fields_with_laddr() {
1532        // completer_id=0x2001, cmpl_stat=0, bcm=0, byte_cnt=0x0FC,
1533        // req_id=0x1234, tag=0xAB, R=0, laddr=100 (0x64)
1534        let bytes = vec![
1535            0x20, 0x01, 0x00, 0xFC, // completer_id=0x2001, status=0, bcm=0, byte_cnt=0x0FC
1536            0x12, 0x34, 0xAB, 0x64, // req_id=0x1234, tag=0xAB, R=0, laddr=0x64
1537        ];
1538        let cmpl = new_cmpl_req(bytes, &TlpFmt::NoDataHeader3DW);
1539        assert_eq!(cmpl.cmpl_id(), 0x2001);
1540        assert_eq!(cmpl.byte_cnt(), 0x0FC);
1541        assert_eq!(cmpl.req_id(), 0x1234);
1542        assert_eq!(cmpl.tag(), 0xAB);
1543        assert_eq!(cmpl.laddr(), 0x64);
1544    }
1545
1546    #[test]
1547    fn atomic_fetchadd_rejects_invalid_operand_length() {
1548        // FetchAdd 3DW expects exactly 12 bytes (8 hdr + 4 operand).
1549        // A 14-byte payload (8 hdr + 6-byte "bad" operand) must be rejected.
1550        let bad = [
1551            0x01, 0x00, 0x01, 0x00, // req_id, tag, BE
1552            0x00, 0x00, 0x10, 0x00, // address32
1553            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 bytes instead of 4
1554        ];
1555        let pkt = mk_pkt(0b010, 0b01100, &bad);
1556        assert_eq!(new_atomic_req(&pkt).unwrap_err(), TlpError::InvalidLength);
1557    }
1558
1559    // ── MessageReqDW24: DW3/DW4 full 32-bit decode ───────────────────────────
1560
1561    #[test]
1562    fn message_dw3_preserves_upper_16_bits() {
1563        // DW3 = 0xDEAD_BEEF — upper 16 bits (0xDEAD) must survive
1564        let bytes = vec![
1565            0x00, 0x00, 0x00, 0x00, // req_id, tag, msg_code
1566            0xDE, 0xAD, 0xBE, 0xEF, // DW3
1567            0x00, 0x00, 0x00, 0x00, // DW4
1568        ];
1569        let msg = new_msg_req(bytes, &TlpFmt::NoDataHeader3DW);
1570        assert_eq!(msg.dw3(), 0xDEAD_BEEF);
1571    }
1572
1573    #[test]
1574    fn message_dw4_preserves_upper_16_bits() {
1575        // DW4 = 0xCAFE_BABE — upper 16 bits (0xCAFE) must survive
1576        let bytes = vec![
1577            0x00, 0x00, 0x00, 0x00, // req_id, tag, msg_code
1578            0x00, 0x00, 0x00, 0x00, // DW3
1579            0xCA, 0xFE, 0xBA, 0xBE, // DW4
1580        ];
1581        let msg = new_msg_req(bytes, &TlpFmt::NoDataHeader3DW);
1582        assert_eq!(msg.dw4(), 0xCAFE_BABE);
1583    }
1584
1585    #[test]
1586    fn message_dw3_dw4_all_bits_set() {
1587        // Both DW3 and DW4 = 0xFFFF_FFFF
1588        let bytes = vec![
1589            0x00, 0x00, 0x00, 0x00,
1590            0xFF, 0xFF, 0xFF, 0xFF,
1591            0xFF, 0xFF, 0xFF, 0xFF,
1592        ];
1593        let msg = new_msg_req(bytes, &TlpFmt::NoDataHeader3DW);
1594        assert_eq!(msg.dw3(), 0xFFFF_FFFF);
1595        assert_eq!(msg.dw4(), 0xFFFF_FFFF);
1596    }
1597
1598    #[test]
1599    fn message_request_full_fields() {
1600        // req_id=0xABCD, tag=0x42, msg_code=0x7F, DW3=0x1234_5678, DW4=0x9ABC_DEF0
1601        let bytes = vec![
1602            0xAB, 0xCD, 0x42, 0x7F,
1603            0x12, 0x34, 0x56, 0x78,
1604            0x9A, 0xBC, 0xDE, 0xF0,
1605        ];
1606        let msg = new_msg_req(bytes, &TlpFmt::NoDataHeader3DW);
1607        assert_eq!(msg.req_id(),   0xABCD);
1608        assert_eq!(msg.tag(),      0x42);
1609        assert_eq!(msg.msg_code(), 0x7F);
1610        assert_eq!(msg.dw3(),      0x1234_5678);
1611        assert_eq!(msg.dw4(),      0x9ABC_DEF0);
1612    }
1613}
1614