1use std::collections::HashSet;
5use std::fmt;
6
7use thiserror::Error;
8
9use super::addr::{LaneAddr, LocationAddr, MoveType};
10use super::types::{ArchSpec, Bus, Word};
11use super::validate::ArchSpecError;
12
13#[derive(Debug, Error)]
15pub enum ArchSpecLoadError {
16 #[error("JSON parse error: {0}")]
17 Json(#[from] serde_json::Error),
18
19 #[error("validation errors: {0:?}")]
20 Validation(Vec<ArchSpecError>),
21}
22
23impl From<Vec<ArchSpecError>> for ArchSpecLoadError {
24 fn from(errors: Vec<ArchSpecError>) -> Self {
25 ArchSpecLoadError::Validation(errors)
26 }
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum LocationGroupError {
33 DuplicateAddress { address: u32 },
35 InvalidAddress { word_id: u32, site_id: u32 },
37}
38
39impl fmt::Display for LocationGroupError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 LocationGroupError::DuplicateAddress { address } => {
43 let addr = LocationAddr::decode(*address);
44 write!(
45 f,
46 "duplicate location address word_id={}, site_id={}",
47 addr.word_id, addr.site_id
48 )
49 }
50 LocationGroupError::InvalidAddress { word_id, site_id } => {
51 write!(
52 f,
53 "invalid location word_id={}, site_id={}",
54 word_id, site_id
55 )
56 }
57 }
58 }
59}
60
61impl std::error::Error for LocationGroupError {}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum LaneGroupError {
65 DuplicateAddress { address: (u32, u32) },
67 InvalidLane { message: String },
69 Inconsistent { message: String },
71 WordNotInSiteBusList { word_id: u32 },
73 SiteNotInWordBusList { site_id: u32 },
75 AODConstraintViolation { message: String },
77}
78
79impl fmt::Display for LaneGroupError {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 LaneGroupError::DuplicateAddress { address } => {
83 let combined = (address.0 as u64) | ((address.1 as u64) << 32);
84 write!(f, "duplicate lane address 0x{:016x}", combined)
85 }
86 LaneGroupError::InvalidLane { message } => {
87 write!(f, "invalid lane: {}", message)
88 }
89 LaneGroupError::Inconsistent { message } => {
90 write!(f, "lane group inconsistent: {}", message)
91 }
92 LaneGroupError::WordNotInSiteBusList { word_id } => {
93 write!(f, "word_id {} not in words_with_site_buses", word_id)
94 }
95 LaneGroupError::SiteNotInWordBusList { site_id } => {
96 write!(f, "site_id {} not in sites_with_word_buses", site_id)
97 }
98 LaneGroupError::AODConstraintViolation { message } => {
99 write!(f, "AOD constraint violation: {}", message)
100 }
101 }
102 }
103}
104
105impl std::error::Error for LaneGroupError {}
106
107impl Bus {
110 pub fn resolve_forward(&self, src: u32) -> Option<u32> {
114 self.src
115 .iter()
116 .position(|&s| s == src)
117 .and_then(|i| self.dst.get(i).copied())
118 }
119
120 pub fn resolve_backward(&self, dst: u32) -> Option<u32> {
124 self.dst
125 .iter()
126 .position(|&d| d == dst)
127 .and_then(|i| self.src.get(i).copied())
128 }
129}
130
131impl Word {
134 pub fn site_position(&self, site_idx: usize) -> Option<(f64, f64)> {
136 let pair = self.site_indices.get(site_idx)?;
137 let x = self.positions.x_position(pair[0] as usize)?;
138 let y = self.positions.y_position(pair[1] as usize)?;
139 Some((x, y))
140 }
141}
142
143impl ArchSpec {
146 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
150 serde_json::from_str(json)
151 }
152
153 pub fn from_json_validated(json: &str) -> Result<Self, ArchSpecLoadError> {
155 let spec = Self::from_json(json)?;
156 spec.validate()?;
157 Ok(spec)
158 }
159
160 pub fn word_by_id(&self, id: u32) -> Option<&Word> {
163 self.geometry.words.get(id as usize)
164 }
165
166 pub fn zone_by_id(&self, id: u32) -> Option<&super::types::Zone> {
167 self.zones.get(id as usize)
168 }
169
170 pub fn site_bus_by_id(&self, id: u32) -> Option<&Bus> {
171 self.buses.site_buses.get(id as usize)
172 }
173
174 pub fn word_bus_by_id(&self, id: u32) -> Option<&Bus> {
175 self.buses.word_buses.get(id as usize)
176 }
177
178 pub fn location_position(&self, loc: &crate::arch::addr::LocationAddr) -> Option<(f64, f64)> {
182 let word = self.word_by_id(loc.word_id)?;
183 word.site_position(loc.site_id as usize)
184 }
185
186 pub fn lane_endpoints(
191 &self,
192 lane: &crate::arch::addr::LaneAddr,
193 ) -> Option<(LocationAddr, LocationAddr)> {
194 use crate::arch::addr::{Direction, MoveType};
195
196 if !self.check_lane(lane).is_empty() {
199 return None;
200 }
201
202 let fwd_src = LocationAddr {
206 word_id: lane.word_id,
207 site_id: lane.site_id,
208 };
209
210 let fwd_dst = match lane.move_type {
211 MoveType::SiteBus => {
212 let bus = self.site_bus_by_id(lane.bus_id)?;
213 let dst_site = bus.resolve_forward(lane.site_id)?;
214 LocationAddr {
215 word_id: lane.word_id,
216 site_id: dst_site,
217 }
218 }
219 MoveType::WordBus => {
220 let bus = self.word_bus_by_id(lane.bus_id)?;
221 let dst_word = bus.resolve_forward(lane.word_id)?;
222 LocationAddr {
223 word_id: dst_word,
224 site_id: lane.site_id,
225 }
226 }
227 };
228
229 match lane.direction {
230 Direction::Forward => Some((fwd_src, fwd_dst)),
231 Direction::Backward => Some((fwd_dst, fwd_src)),
232 }
233 }
234
235 pub fn get_blockaded_location(&self, loc: &LocationAddr) -> Option<LocationAddr> {
241 let word = self.word_by_id(loc.word_id)?;
242 let cz_pairs = word.has_cz.as_ref()?;
243 let pair = cz_pairs.get(loc.site_id as usize)?;
244 let result = LocationAddr {
245 word_id: pair[0],
246 site_id: pair[1],
247 };
248 if self.check_location(&result).is_some() {
250 return None;
251 }
252 Some(result)
253 }
254
255 pub fn check_location(&self, loc: &crate::arch::addr::LocationAddr) -> Option<String> {
259 let num_words = self.geometry.words.len() as u32;
260 let sites_per_word = self.geometry.sites_per_word;
261 if loc.word_id >= num_words || loc.site_id >= sites_per_word {
262 Some(format!(
263 "invalid location word_id={}, site_id={}",
264 loc.word_id, loc.site_id
265 ))
266 } else {
267 None
268 }
269 }
270
271 pub fn check_lane(&self, addr: &LaneAddr) -> Vec<String> {
279 let num_words = self.geometry.words.len() as u32;
280 let sites_per_word = self.geometry.sites_per_word;
281 let mut errors = Vec::new();
282
283 match addr.move_type {
284 MoveType::SiteBus => {
285 if addr.word_id >= num_words {
286 errors.push(format!("word_id {} out of range", addr.word_id));
287 } else if !self.words_with_site_buses.contains(&addr.word_id) {
288 errors.push(format!(
289 "word_id {} not in words_with_site_buses",
290 addr.word_id
291 ));
292 }
293 if addr.site_id >= sites_per_word {
294 errors.push(format!("site_id {} out of range", addr.site_id));
295 }
296 if let Some(bus) = self.site_bus_by_id(addr.bus_id) {
297 if errors.is_empty() && bus.resolve_forward(addr.site_id).is_none() {
298 errors.push(format!(
299 "site_id {} is not a valid source for site_bus {}",
300 addr.site_id, addr.bus_id
301 ));
302 }
303 } else {
304 errors.push(format!("unknown site_bus id {}", addr.bus_id));
305 }
306 }
307 MoveType::WordBus => {
308 if addr.word_id >= num_words {
309 errors.push(format!("word_id {} out of range", addr.word_id));
310 }
311 if addr.site_id >= sites_per_word {
312 errors.push(format!("site_id {} out of range", addr.site_id));
313 } else if !self.sites_with_word_buses.contains(&addr.site_id) {
314 errors.push(format!(
315 "site_id {} not in sites_with_word_buses",
316 addr.site_id
317 ));
318 }
319 if let Some(bus) = self.word_bus_by_id(addr.bus_id) {
320 if errors.is_empty() && bus.resolve_forward(addr.word_id).is_none() {
321 errors.push(format!(
322 "word_id {} is not a valid source for word_bus {}",
323 addr.word_id, addr.bus_id
324 ));
325 }
326 } else {
327 errors.push(format!("unknown word_bus id {}", addr.bus_id));
328 }
329 }
330 }
331 errors
332 }
333
334 pub fn check_zone(&self, zone: &crate::arch::addr::ZoneAddr) -> Option<String> {
336 if self.zone_by_id(zone.zone_id).is_none() {
337 Some(format!("invalid zone_id={}", zone.zone_id))
338 } else {
339 None
340 }
341 }
342
343 pub fn check_lane_group_consistency(&self, lanes: &[LaneAddr]) -> Vec<String> {
347 if lanes.is_empty() {
348 return vec![];
349 }
350 let first = &lanes[0];
351 let mut errors = Vec::new();
352
353 for lane in &lanes[1..] {
354 if lane.bus_id != first.bus_id {
355 errors.push(format!(
356 "bus_id mismatch: expected {}, got {}",
357 first.bus_id, lane.bus_id
358 ));
359 }
360 if lane.move_type != first.move_type {
361 errors.push(format!(
362 "move_type mismatch: expected {:?}, got {:?}",
363 first.move_type, lane.move_type
364 ));
365 }
366 if lane.direction != first.direction {
367 errors.push(format!(
368 "direction mismatch: expected {:?}, got {:?}",
369 first.direction, lane.direction
370 ));
371 }
372 }
373
374 errors
375 }
376
377 pub fn check_lane_group_membership(&self, lanes: &[LaneAddr]) -> (Vec<u32>, Vec<u32>) {
380 use std::collections::BTreeSet;
381
382 let mut bad_words = BTreeSet::new();
383 let mut bad_sites = BTreeSet::new();
384
385 for lane in lanes {
386 match lane.move_type {
387 MoveType::SiteBus => {
388 if !self.words_with_site_buses.contains(&lane.word_id) {
389 bad_words.insert(lane.word_id);
390 }
391 }
392 MoveType::WordBus => {
393 if !self.sites_with_word_buses.contains(&lane.site_id) {
394 bad_sites.insert(lane.site_id);
395 }
396 }
397 }
398 }
399
400 (
401 bad_words.into_iter().collect(),
402 bad_sites.into_iter().collect(),
403 )
404 }
405
406 pub fn check_locations(&self, locations: &[LocationAddr]) -> Vec<LocationGroupError> {
409 let mut errors = Vec::new();
410
411 let mut checked = HashSet::new();
413 for loc in locations {
414 let bits = loc.encode();
415 if checked.insert(bits) && self.check_location(loc).is_some() {
416 errors.push(LocationGroupError::InvalidAddress {
417 word_id: loc.word_id,
418 site_id: loc.site_id,
419 });
420 }
421 }
422
423 let mut seen = HashSet::new();
425 let mut reported = HashSet::new();
426 for loc in locations {
427 let bits = loc.encode();
428 if !seen.insert(bits) && reported.insert(bits) {
429 errors.push(LocationGroupError::DuplicateAddress { address: bits });
430 }
431 }
432
433 errors
434 }
435
436 pub fn check_lanes(&self, lanes: &[LaneAddr]) -> Vec<LaneGroupError> {
440 let mut errors = Vec::new();
441
442 let mut checked = HashSet::new();
444 for lane in lanes {
445 let bits = lane.encode();
446 if checked.insert(bits) {
447 for msg in self.check_lane(lane) {
448 errors.push(LaneGroupError::InvalidLane { message: msg });
449 }
450 }
451 }
452
453 let mut seen = HashSet::new();
455 let mut reported = HashSet::new();
456 for lane in lanes {
457 let pair = lane.encode();
458 if !seen.insert(pair) && reported.insert(pair) {
459 errors.push(LaneGroupError::DuplicateAddress { address: pair });
460 }
461 }
462
463 if lanes.len() > 1 {
465 for msg in self.check_lane_group_consistency(lanes) {
466 errors.push(LaneGroupError::Inconsistent { message: msg });
467 }
468 let (bad_words, bad_sites) = self.check_lane_group_membership(lanes);
469 for word_id in bad_words {
470 errors.push(LaneGroupError::WordNotInSiteBusList { word_id });
471 }
472 for site_id in bad_sites {
473 errors.push(LaneGroupError::SiteNotInWordBusList { site_id });
474 }
475 for msg in self.check_lane_group_geometry(lanes) {
476 errors.push(LaneGroupError::AODConstraintViolation { message: msg });
477 }
478 }
479
480 errors
481 }
482
483 pub fn check_lane_group_geometry(&self, lanes: &[LaneAddr]) -> Vec<String> {
486 use std::collections::BTreeSet;
487
488 let positions: Vec<(f64, f64)> = lanes
489 .iter()
490 .filter_map(|lane| {
491 let loc = crate::arch::addr::LocationAddr {
492 word_id: lane.word_id,
493 site_id: lane.site_id,
494 };
495 self.location_position(&loc)
496 })
497 .collect();
498
499 if positions.len() != lanes.len() {
500 return vec!["some lane positions could not be resolved".to_string()];
501 }
502
503 let unique_x: BTreeSet<u64> = positions.iter().map(|(x, _)| x.to_bits()).collect();
504 let unique_y: BTreeSet<u64> = positions.iter().map(|(_, y)| y.to_bits()).collect();
505
506 let expected: BTreeSet<(u64, u64)> = unique_x
507 .iter()
508 .flat_map(|x| unique_y.iter().map(move |y| (*x, *y)))
509 .collect();
510
511 let actual: BTreeSet<(u64, u64)> = positions
512 .iter()
513 .map(|(x, y)| (x.to_bits(), y.to_bits()))
514 .collect();
515
516 if actual != expected {
517 vec![format!(
518 "lanes do not form a complete grid: expected {} positions ({}x × {}y), got {} unique positions",
519 expected.len(),
520 unique_x.len(),
521 unique_y.len(),
522 actual.len()
523 )]
524 } else {
525 vec![]
526 }
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use crate::arch::example_arch_spec;
533
534 #[test]
535 fn word_by_id_found() {
536 let spec = example_arch_spec();
537 let word = spec.word_by_id(0).unwrap();
538 assert_eq!(word.site_indices.len(), 10);
539 }
540
541 #[test]
542 fn word_by_id_not_found() {
543 let spec = example_arch_spec();
544 assert!(spec.word_by_id(99).is_none());
545 }
546
547 #[test]
548 fn zone_by_id_found() {
549 let spec = example_arch_spec();
550 let zone = spec.zone_by_id(0).unwrap();
551 assert_eq!(zone.words, vec![0, 1]);
552 }
553
554 #[test]
555 fn zone_by_id_not_found() {
556 let spec = example_arch_spec();
557 assert!(spec.zone_by_id(99).is_none());
558 }
559
560 #[test]
561 fn site_bus_by_id_found() {
562 let spec = example_arch_spec();
563 let bus = spec.site_bus_by_id(0).unwrap();
564 assert_eq!(bus.src, vec![0, 1, 2, 3, 4]);
565 }
566
567 #[test]
568 fn site_bus_by_id_not_found() {
569 let spec = example_arch_spec();
570 assert!(spec.site_bus_by_id(99).is_none());
571 }
572
573 #[test]
574 fn word_bus_by_id_found() {
575 let spec = example_arch_spec();
576 let bus = spec.word_bus_by_id(0).unwrap();
577 assert_eq!(bus.src, vec![0]);
578 assert_eq!(bus.dst, vec![1]);
579 }
580
581 #[test]
582 fn word_bus_by_id_not_found() {
583 let spec = example_arch_spec();
584 assert!(spec.word_bus_by_id(99).is_none());
585 }
586
587 #[test]
588 fn site_bus_resolve_forward() {
589 let spec = example_arch_spec();
590 let bus = spec.site_bus_by_id(0).unwrap();
591 assert_eq!(bus.resolve_forward(0), Some(5));
592 assert_eq!(bus.resolve_forward(4), Some(9));
593 assert_eq!(bus.resolve_forward(99), None);
594 }
595
596 #[test]
597 fn site_bus_resolve_backward() {
598 let spec = example_arch_spec();
599 let bus = spec.site_bus_by_id(0).unwrap();
600 assert_eq!(bus.resolve_backward(5), Some(0));
601 assert_eq!(bus.resolve_backward(9), Some(4));
602 assert_eq!(bus.resolve_backward(99), None);
603 }
604
605 #[test]
606 fn word_bus_resolve_forward() {
607 let spec = example_arch_spec();
608 let bus = spec.word_bus_by_id(0).unwrap();
609 assert_eq!(bus.resolve_forward(0), Some(1));
610 assert_eq!(bus.resolve_forward(99), None);
611 }
612
613 #[test]
614 fn word_bus_resolve_backward() {
615 let spec = example_arch_spec();
616 let bus = spec.word_bus_by_id(0).unwrap();
617 assert_eq!(bus.resolve_backward(1), Some(0));
618 assert_eq!(bus.resolve_backward(99), None);
619 }
620
621 #[test]
622 fn site_position_valid() {
623 let spec = example_arch_spec();
624 let word = spec.word_by_id(0).unwrap();
625 assert_eq!(word.site_position(0), Some((1.0, 2.5)));
627 assert_eq!(word.site_position(5), Some((1.0, 5.0)));
629 assert_eq!(word.site_position(4), Some((9.0, 2.5)));
631 }
632
633 #[test]
634 fn site_position_out_of_range() {
635 let spec = example_arch_spec();
636 let word = spec.word_by_id(0).unwrap();
637 assert!(word.site_position(99).is_none());
638 }
639
640 #[test]
641 fn location_position_valid() {
642 let spec = example_arch_spec();
643 let loc = crate::arch::addr::LocationAddr {
644 word_id: 0,
645 site_id: 0,
646 };
647 assert_eq!(spec.location_position(&loc), Some((1.0, 2.5)));
648 }
649
650 #[test]
651 fn location_position_invalid_word() {
652 let spec = example_arch_spec();
653 let loc = crate::arch::addr::LocationAddr {
654 word_id: 99,
655 site_id: 0,
656 };
657 assert!(spec.location_position(&loc).is_none());
658 }
659
660 #[test]
661 fn location_position_invalid_site() {
662 let spec = example_arch_spec();
663 let loc = crate::arch::addr::LocationAddr {
664 word_id: 0,
665 site_id: 99,
666 };
667 assert!(spec.location_position(&loc).is_none());
668 }
669
670 #[test]
671 fn from_json_valid() {
672 let json = serde_json::to_string(&example_arch_spec()).unwrap();
673 let spec = super::super::ArchSpec::from_json(&json).unwrap();
674 assert_eq!(spec.version, crate::version::Version::new(1, 0));
675 }
676
677 #[test]
678 fn from_json_validated_valid() {
679 let json = serde_json::to_string(&example_arch_spec()).unwrap();
680 let spec = super::super::ArchSpec::from_json_validated(&json).unwrap();
681 assert_eq!(spec.version, crate::version::Version::new(1, 0));
682 }
683
684 #[test]
685 fn check_lane_group_consistency_empty() {
686 let spec = example_arch_spec();
687 assert!(spec.check_lane_group_consistency(&[]).is_empty());
688 }
689
690 #[test]
691 fn from_json_validated_invalid() {
692 let json = r#"{"version": "1.0"}"#;
694 let result = super::super::ArchSpec::from_json_validated(json);
695 assert!(result.is_err());
696 }
697
698 #[test]
699 fn get_blockaded_location_valid() {
700 let spec = example_arch_spec();
701 let loc = crate::arch::addr::LocationAddr {
703 word_id: 0,
704 site_id: 0,
705 };
706 let pair = spec.get_blockaded_location(&loc).unwrap();
707 assert_eq!(pair.word_id, 0);
708 assert_eq!(pair.site_id, 5);
709 }
710
711 #[test]
712 fn get_blockaded_location_reverse() {
713 let spec = example_arch_spec();
714 let loc = crate::arch::addr::LocationAddr {
716 word_id: 0,
717 site_id: 5,
718 };
719 let pair = spec.get_blockaded_location(&loc).unwrap();
720 assert_eq!(pair.word_id, 0);
721 assert_eq!(pair.site_id, 0);
722 }
723
724 #[test]
725 fn get_blockaded_location_invalid_word() {
726 let spec = example_arch_spec();
727 let loc = crate::arch::addr::LocationAddr {
728 word_id: 99,
729 site_id: 0,
730 };
731 assert!(spec.get_blockaded_location(&loc).is_none());
732 }
733
734 #[test]
735 fn get_blockaded_location_invalid_site() {
736 let spec = example_arch_spec();
737 let loc = crate::arch::addr::LocationAddr {
738 word_id: 0,
739 site_id: 99,
740 };
741 assert!(spec.get_blockaded_location(&loc).is_none());
742 }
743
744 #[test]
747 fn lane_endpoints_site_bus_forward() {
748 let spec = example_arch_spec();
749 let lane = crate::arch::addr::LaneAddr {
751 direction: crate::arch::addr::Direction::Forward,
752 move_type: crate::arch::addr::MoveType::SiteBus,
753 word_id: 0,
754 site_id: 0,
755 bus_id: 0,
756 };
757 let (src, dst) = spec.lane_endpoints(&lane).unwrap();
758 assert_eq!(src.word_id, 0);
759 assert_eq!(src.site_id, 0);
760 assert_eq!(dst.word_id, 0);
761 assert_eq!(dst.site_id, 5);
762 }
763
764 #[test]
765 fn lane_endpoints_site_bus_backward() {
766 let spec = example_arch_spec();
767 let lane = crate::arch::addr::LaneAddr {
769 direction: crate::arch::addr::Direction::Backward,
770 move_type: crate::arch::addr::MoveType::SiteBus,
771 word_id: 0,
772 site_id: 0,
773 bus_id: 0,
774 };
775 let (src, dst) = spec.lane_endpoints(&lane).unwrap();
776 assert_eq!(src.word_id, 0);
778 assert_eq!(src.site_id, 5);
779 assert_eq!(dst.word_id, 0);
780 assert_eq!(dst.site_id, 0);
781 }
782
783 #[test]
784 fn lane_endpoints_word_bus_forward() {
785 let spec = example_arch_spec();
786 let lane = crate::arch::addr::LaneAddr {
788 direction: crate::arch::addr::Direction::Forward,
789 move_type: crate::arch::addr::MoveType::WordBus,
790 word_id: 0,
791 site_id: 5,
792 bus_id: 0,
793 };
794 let (src, dst) = spec.lane_endpoints(&lane).unwrap();
795 assert_eq!(src.word_id, 0);
796 assert_eq!(src.site_id, 5);
797 assert_eq!(dst.word_id, 1);
798 assert_eq!(dst.site_id, 5);
799 }
800
801 #[test]
802 fn lane_endpoints_word_bus_backward() {
803 let spec = example_arch_spec();
804 let lane = crate::arch::addr::LaneAddr {
806 direction: crate::arch::addr::Direction::Backward,
807 move_type: crate::arch::addr::MoveType::WordBus,
808 word_id: 0,
809 site_id: 5,
810 bus_id: 0,
811 };
812 let (src, dst) = spec.lane_endpoints(&lane).unwrap();
813 assert_eq!(src.word_id, 1);
815 assert_eq!(src.site_id, 5);
816 assert_eq!(dst.word_id, 0);
817 assert_eq!(dst.site_id, 5);
818 }
819
820 #[test]
821 fn lane_endpoints_invalid_bus_returns_none() {
822 let spec = example_arch_spec();
823 let lane = crate::arch::addr::LaneAddr {
824 direction: crate::arch::addr::Direction::Forward,
825 move_type: crate::arch::addr::MoveType::SiteBus,
826 word_id: 0,
827 site_id: 0,
828 bus_id: 99,
829 };
830 assert!(spec.lane_endpoints(&lane).is_none());
831 }
832
833 #[test]
834 fn lane_endpoints_invalid_site_not_in_bus_returns_none() {
835 let spec = example_arch_spec();
836 let lane = crate::arch::addr::LaneAddr {
838 direction: crate::arch::addr::Direction::Forward,
839 move_type: crate::arch::addr::MoveType::SiteBus,
840 word_id: 0,
841 site_id: 5,
842 bus_id: 0,
843 };
844 assert!(spec.lane_endpoints(&lane).is_none());
845 }
846
847 #[test]
850 fn check_lane_valid_forward() {
851 let spec = example_arch_spec();
852 let lane = crate::arch::addr::LaneAddr {
853 direction: crate::arch::addr::Direction::Forward,
854 move_type: crate::arch::addr::MoveType::SiteBus,
855 word_id: 0,
856 site_id: 0,
857 bus_id: 0,
858 };
859 assert!(spec.check_lane(&lane).is_empty());
860 }
861
862 #[test]
863 fn check_lane_valid_backward() {
864 let spec = example_arch_spec();
865 let lane = crate::arch::addr::LaneAddr {
867 direction: crate::arch::addr::Direction::Backward,
868 move_type: crate::arch::addr::MoveType::SiteBus,
869 word_id: 0,
870 site_id: 0,
871 bus_id: 0,
872 };
873 assert!(spec.check_lane(&lane).is_empty());
874 }
875
876 #[test]
877 fn check_lane_destination_site_rejected() {
878 let spec = example_arch_spec();
879 let lane = crate::arch::addr::LaneAddr {
881 direction: crate::arch::addr::Direction::Forward,
882 move_type: crate::arch::addr::MoveType::SiteBus,
883 word_id: 0,
884 site_id: 5,
885 bus_id: 0,
886 };
887 let errors = spec.check_lane(&lane);
888 assert!(!errors.is_empty());
889 assert!(errors[0].contains("not a valid source"));
890 }
891
892 #[test]
893 fn check_lane_destination_site_backward_also_rejected() {
894 let spec = example_arch_spec();
895 let lane = crate::arch::addr::LaneAddr {
897 direction: crate::arch::addr::Direction::Backward,
898 move_type: crate::arch::addr::MoveType::SiteBus,
899 word_id: 0,
900 site_id: 5,
901 bus_id: 0,
902 };
903 let errors = spec.check_lane(&lane);
904 assert!(!errors.is_empty());
905 assert!(errors[0].contains("not a valid source"));
906 }
907
908 #[test]
909 fn check_lane_invalid_bus() {
910 let spec = example_arch_spec();
911 let lane = crate::arch::addr::LaneAddr {
912 direction: crate::arch::addr::Direction::Forward,
913 move_type: crate::arch::addr::MoveType::SiteBus,
914 word_id: 0,
915 site_id: 0,
916 bus_id: 99,
917 };
918 let errors = spec.check_lane(&lane);
919 assert!(!errors.is_empty());
920 }
921
922 #[test]
923 fn check_lane_word_not_in_site_bus_list() {
924 let mut spec = example_arch_spec();
925 spec.words_with_site_buses.retain(|&w| w != 0);
927 let lane = crate::arch::addr::LaneAddr {
928 direction: crate::arch::addr::Direction::Forward,
929 move_type: crate::arch::addr::MoveType::SiteBus,
930 word_id: 0,
931 site_id: 0,
932 bus_id: 0,
933 };
934 let errors = spec.check_lane(&lane);
935 assert!(!errors.is_empty());
936 assert!(errors[0].contains("words_with_site_buses"));
937 }
938
939 #[test]
940 fn check_lane_site_not_in_word_bus_list() {
941 let spec = example_arch_spec();
942 let lane = crate::arch::addr::LaneAddr {
944 direction: crate::arch::addr::Direction::Forward,
945 move_type: crate::arch::addr::MoveType::WordBus,
946 word_id: 0,
947 site_id: 0,
948 bus_id: 0,
949 };
950 let errors = spec.check_lane(&lane);
951 assert!(!errors.is_empty());
952 assert!(errors[0].contains("sites_with_word_buses"));
953 }
954}