@@ -13,6 +13,7 @@ use crate::peripherals::uart::UartRegisterLayout;
1313use crate :: { Bus , DmaRequest , Peripheral , SimResult , SimulationError } ;
1414use anyhow:: Context ;
1515use labwired_config:: { parse_size, ChipDescriptor , PeripheralConfig , SystemManifest } ;
16+ use std:: cell:: Cell ;
1617use std:: path:: { Path , PathBuf } ;
1718use std:: str:: FromStr ;
1819use std:: sync:: atomic:: Ordering ;
@@ -33,6 +34,15 @@ pub struct SystemBus {
3334 pub peripherals : Vec < PeripheralEntry > ,
3435 pub nvic : Option < Arc < NvicState > > ,
3536 pub observers : Vec < Arc < dyn crate :: SimulationObserver > > ,
37+ peripheral_ranges : Vec < PeripheralRange > ,
38+ peripheral_hint : Cell < Option < usize > > ,
39+ }
40+
41+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
42+ struct PeripheralRange {
43+ start : u64 ,
44+ end : u64 ,
45+ index : usize ,
3646}
3747
3848#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
@@ -48,7 +58,7 @@ impl Default for SystemBus {
4858}
4959
5060impl SystemBus {
51- fn profile_name < ' a > ( p_cfg : & ' a PeripheralConfig ) -> anyhow:: Result < Option < & ' a str > > {
61+ fn profile_name ( p_cfg : & PeripheralConfig ) -> anyhow:: Result < Option < & str > > {
5262 if let Some ( value) = p_cfg. config . get ( "profile" ) {
5363 return value. as_str ( ) . map ( Some ) . ok_or_else ( || {
5464 anyhow:: anyhow!( "Peripheral '{}' config.profile must be a string" , p_cfg. id)
@@ -88,18 +98,88 @@ impl SystemBus {
8898
8999 fn resolve_peripheral_path ( manifest : & SystemManifest , descriptor_path : & str ) -> PathBuf {
90100 let raw = PathBuf :: from ( descriptor_path) ;
91- if raw. is_absolute ( ) || raw . exists ( ) {
101+ if raw. is_absolute ( ) {
92102 return raw;
93103 }
94104
95105 let chip_path = Path :: new ( & manifest. chip ) ;
96106 let chip_dir = chip_path. parent ( ) . unwrap_or_else ( || Path :: new ( "." ) ) ;
97- chip_dir. join ( descriptor_path)
107+ let chip_relative = chip_dir. join ( descriptor_path) ;
108+ if chip_relative. exists ( ) {
109+ chip_relative
110+ } else {
111+ raw
112+ }
113+ }
114+
115+ fn is_peripheral_addr ( p : & PeripheralEntry , addr : u64 ) -> bool {
116+ addr >= p. base && addr < p. base + p. size
117+ }
118+
119+ fn rebuild_peripheral_ranges ( & mut self ) {
120+ self . peripheral_ranges = self
121+ . peripherals
122+ . iter ( )
123+ . enumerate ( )
124+ . map ( |( index, p) | PeripheralRange {
125+ start : p. base ,
126+ end : p. base . saturating_add ( p. size ) ,
127+ index,
128+ } )
129+ . collect ( ) ;
130+ self . peripheral_ranges . sort_by_key ( |r| r. start ) ;
131+ self . peripheral_hint . set ( None ) ;
132+ }
133+
134+ pub fn refresh_peripheral_index ( & mut self ) {
135+ self . rebuild_peripheral_ranges ( ) ;
136+ }
137+
138+ fn find_peripheral_index ( & self , addr : u64 ) -> Option < usize > {
139+ if let Some ( idx) = self . peripheral_hint . get ( ) {
140+ if let Some ( p) = self . peripherals . get ( idx) {
141+ if Self :: is_peripheral_addr ( p, addr) {
142+ return Some ( idx) ;
143+ }
144+ }
145+ }
146+
147+ let mut idx = if self . peripheral_ranges . len ( ) == self . peripherals . len ( ) {
148+ let pos = self
149+ . peripheral_ranges
150+ . partition_point ( |range| range. start <= addr) ;
151+ if pos == 0 {
152+ None
153+ } else {
154+ let candidate = self . peripheral_ranges [ pos - 1 ] ;
155+ if addr < candidate. end {
156+ Some ( candidate. index )
157+ } else {
158+ None
159+ }
160+ }
161+ } else {
162+ None
163+ } ;
164+
165+ let needs_fallback = match idx. and_then ( |i| self . peripherals . get ( i) ) {
166+ Some ( p) => !Self :: is_peripheral_addr ( p, addr) ,
167+ None => true ,
168+ } ;
169+ if needs_fallback {
170+ idx = self
171+ . peripherals
172+ . iter ( )
173+ . position ( |p| Self :: is_peripheral_addr ( p, addr) ) ;
174+ }
175+
176+ self . peripheral_hint . set ( idx) ;
177+ idx
98178 }
99179
100180 pub fn new ( ) -> Self {
101181 // Default initialization for tests
102- Self {
182+ let mut bus = Self {
103183 flash : LinearMemory :: new ( 1024 * 1024 , 0x0 ) ,
104184 ram : LinearMemory :: new ( 1024 * 1024 , 0x2000_0000 ) ,
105185 peripherals : vec ! [
@@ -211,7 +291,11 @@ impl SystemBus {
211291 ] ,
212292 nvic : None ,
213293 observers : Vec :: new ( ) ,
214- }
294+ peripheral_ranges : Vec :: new ( ) ,
295+ peripheral_hint : Cell :: new ( None ) ,
296+ } ;
297+ bus. rebuild_peripheral_ranges ( ) ;
298+ bus
215299 }
216300
217301 /// Attach a UART TX capture sink to any UART peripherals on this bus.
@@ -239,6 +323,8 @@ impl SystemBus {
239323 peripherals : Vec :: new ( ) ,
240324 nvic : None ,
241325 observers : Vec :: new ( ) ,
326+ peripheral_ranges : Vec :: new ( ) ,
327+ peripheral_hint : Cell :: new ( None ) ,
242328 } ;
243329
244330 for p_cfg in & chip. peripherals {
@@ -415,6 +501,7 @@ impl SystemBus {
415501 } ) ;
416502 }
417503
504+ bus. rebuild_peripheral_ranges ( ) ;
418505 Ok ( bus)
419506 }
420507
@@ -596,10 +683,9 @@ impl crate::Bus for SystemBus {
596683 }
597684
598685 // Dynamic Peripherals
599- for p in & self . peripherals {
600- if addr >= p. base && addr < p. base + p. size {
601- return p. dev . read ( addr - p. base ) ;
602- }
686+ if let Some ( idx) = self . find_peripheral_index ( addr) {
687+ let p = & self . peripherals [ idx] ;
688+ return p. dev . read ( addr - p. base ) ;
603689 }
604690
605691 Err ( SimulationError :: MemoryViolation ( addr) )
@@ -619,10 +705,10 @@ impl crate::Bus for SystemBus {
619705 . or_else ( || self . flash . read_u8 ( addr) )
620706 . or ( flash_alias_old)
621707 . or_else ( || {
622- self . peripherals
623- . iter ( )
624- . find ( |p| addr >= p. base && addr < p . base + p . size )
625- . and_then ( |p| p . dev . peek ( addr - p . base ) )
708+ self . find_peripheral_index ( addr ) . and_then ( |idx| {
709+ let p = & self . peripherals [ idx ] ;
710+ p . dev . peek ( addr - p. base )
711+ } )
626712 } )
627713 . unwrap_or ( 0 ) ;
628714
@@ -637,17 +723,9 @@ impl crate::Bus for SystemBus {
637723 Ok ( ( ) )
638724 } else {
639725 // Dynamic Peripherals
640- let mut found = false ;
641- let mut p_res = Ok ( ( ) ) ;
642- for p in & mut self . peripherals {
643- if addr >= p. base && addr < p. base + p. size {
644- p_res = p. dev . write ( addr - p. base , value) ;
645- found = true ;
646- break ;
647- }
648- }
649- if found {
650- p_res
726+ if let Some ( idx) = self . find_peripheral_index ( addr) {
727+ let p = & mut self . peripherals [ idx] ;
728+ p. dev . write ( addr - p. base , value)
651729 } else {
652730 Err ( SimulationError :: MemoryViolation ( addr) )
653731 }
@@ -804,6 +882,8 @@ mod tests {
804882 peripherals : Vec :: new ( ) ,
805883 nvic : None ,
806884 observers : Vec :: new ( ) ,
885+ peripheral_ranges : Vec :: new ( ) ,
886+ peripheral_hint : Cell :: new ( None ) ,
807887 } ;
808888
809889 bus. flash . write_u8 ( 0x0800_0000 , 0x12 ) ;
@@ -817,4 +897,39 @@ mod tests {
817897 bus. write_u8 ( 0x0000_0001 , 0xAB ) . unwrap ( ) ;
818898 assert_eq ! ( bus. flash. read_u8( 0x0800_0001 ) , Some ( 0xAB ) ) ;
819899 }
900+
901+ #[ test]
902+ fn test_peripheral_range_index_lookup ( ) {
903+ let mut bus = SystemBus {
904+ flash : LinearMemory :: new ( 256 , 0x0800_0000 ) ,
905+ ram : LinearMemory :: new ( 256 , 0x2000_0000 ) ,
906+ peripherals : vec ! [
907+ PeripheralEntry {
908+ name: "high" . to_string( ) ,
909+ base: 0x5000_0000 ,
910+ size: 0x1000 ,
911+ irq: None ,
912+ dev: Box :: new( crate :: peripherals:: uart:: Uart :: new( ) ) ,
913+ } ,
914+ PeripheralEntry {
915+ name: "low" . to_string( ) ,
916+ base: 0x4000_0000 ,
917+ size: 0x1000 ,
918+ irq: None ,
919+ dev: Box :: new( crate :: peripherals:: uart:: Uart :: new( ) ) ,
920+ } ,
921+ ] ,
922+ nvic : None ,
923+ observers : Vec :: new ( ) ,
924+ peripheral_ranges : Vec :: new ( ) ,
925+ peripheral_hint : Cell :: new ( None ) ,
926+ } ;
927+
928+ bus. rebuild_peripheral_ranges ( ) ;
929+ let low_idx = bus. find_peripheral_index ( 0x4000_0004 ) ;
930+ let high_idx = bus. find_peripheral_index ( 0x5000_0004 ) ;
931+
932+ assert_eq ! ( low_idx, Some ( 1 ) ) ;
933+ assert_eq ! ( high_idx, Some ( 0 ) ) ;
934+ }
820935}
0 commit comments