@@ -56,6 +56,37 @@ pub struct ExternalDevice {
5656 pub config : HashMap < String , serde_yaml:: Value > ,
5757}
5858
59+ #[ derive( Debug , Serialize , Deserialize , Clone , Copy , PartialEq , Eq ) ]
60+ #[ serde( rename_all = "lowercase" ) ]
61+ pub enum BoardIoKind {
62+ Led ,
63+ Button ,
64+ }
65+
66+ #[ derive( Debug , Serialize , Deserialize , Clone , Copy , PartialEq , Eq , Default ) ]
67+ #[ serde( rename_all = "lowercase" ) ]
68+ pub enum BoardIoSignal {
69+ #[ default]
70+ Output ,
71+ Input ,
72+ }
73+
74+ fn default_true ( ) -> bool {
75+ true
76+ }
77+
78+ #[ derive( Debug , Serialize , Deserialize , Clone ) ]
79+ pub struct BoardIoBinding {
80+ pub id : String ,
81+ pub kind : BoardIoKind ,
82+ pub peripheral : String ,
83+ pub pin : u8 ,
84+ #[ serde( default ) ]
85+ pub signal : BoardIoSignal ,
86+ #[ serde( default = "default_true" ) ]
87+ pub active_high : bool ,
88+ }
89+
5990#[ derive( Debug , Serialize , Deserialize , Clone ) ]
6091pub struct SystemManifest {
6192 pub name : String ,
@@ -64,6 +95,8 @@ pub struct SystemManifest {
6495 pub memory_overrides : HashMap < String , String > ,
6596 #[ serde( default ) ]
6697 pub external_devices : Vec < ExternalDevice > ,
98+ #[ serde( default ) ]
99+ pub board_io : Vec < BoardIoBinding > ,
67100}
68101
69102impl ChipDescriptor {
@@ -239,15 +272,59 @@ impl From<labwired_ir::IrPeripheral> for PeripheralDescriptor {
239272 description : f. description ,
240273 } )
241274 . collect ( ) ,
242- side_effects : None , // IR doesn't explicitly model side effects yet
275+ side_effects : r. side_effects . map ( |se| SideEffectsDescriptor {
276+ read_action : se. read_action . and_then ( |s| match s. as_str ( ) {
277+ "clear" => Some ( ReadAction :: Clear ) ,
278+ "none" => Some ( ReadAction :: None ) ,
279+ _ => None ,
280+ } ) ,
281+ write_action : se. write_action . and_then ( |s| match s. as_str ( ) {
282+ "one_to_clear" | "oneToClear" | "w1c" => {
283+ Some ( WriteAction :: WriteOneToClear )
284+ }
285+ "zero_to_clear" | "zeroToClear" | "w0c" => {
286+ Some ( WriteAction :: WriteZeroToClear )
287+ }
288+ "none" => Some ( WriteAction :: None ) ,
289+ _ => None ,
290+ } ) ,
291+ on_read : None ,
292+ on_write : None ,
293+ } ) ,
243294 } )
244295 . collect ( ) ,
245296 interrupts : if interrupts. is_empty ( ) {
246297 None
247298 } else {
248299 Some ( interrupts)
249300 } ,
250- timing : None ,
301+ timing : if ir. timing . is_empty ( ) {
302+ None
303+ } else {
304+ let mut timing = Vec :: new ( ) ;
305+ for t in ir. timing {
306+ // Try to convert JSON trigger/action to enums
307+ let trigger: Result < TimingTrigger , _ > = serde_json:: from_value ( t. trigger ) ;
308+ let action: Result < TimingAction , _ > = serde_json:: from_value ( t. action ) ;
309+
310+ if let ( Ok ( trig) , Ok ( act) ) = ( trigger, action) {
311+ timing. push ( TimingDescriptor {
312+ id : t. id ,
313+ trigger : trig,
314+ delay_cycles : t. delay_cycles ,
315+ action : act,
316+ interrupt : t. interrupt ,
317+ } ) ;
318+ } else {
319+ tracing:: warn!( "Failed to convert IR timing hook '{}' to config" , t. id) ;
320+ }
321+ }
322+ if timing. is_empty ( ) {
323+ None
324+ } else {
325+ Some ( timing)
326+ }
327+ } ,
251328 }
252329 }
253330}
0 commit comments