diff --git a/Cargo.toml b/Cargo.toml
index 34df35b336ad813a6efb440fe013999d7d0801ff..8be19118503f77a92e584b21d3599cd330bfbb7f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,11 +9,7 @@ description = "(Another) DSL/library for specifying and analyzing SoCs for syste
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [[bin]]
-name = "imx8"
-path = "src/bin/imx8.rs"
-
-[[bin]]
-name = "toy_qualpwn"
+name = "repl"
 
 [[bin]]
 name = "mmu"
diff --git a/src/bin/imx8.rs b/src/bin/imx8.rs
deleted file mode 100644
index 9d106972455ca783c9f96fd1dd0e24bfb4ea0f24..0000000000000000000000000000000000000000
--- a/src/bin/imx8.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-use log::info;
-
-use std::collections::{BTreeSet,BTreeMap};
-use std::fs::File;
-
-use sockeye3::glas::{self, *};
-use sockeye3::specs::examples::*;
-use sockeye3::output;
-use sockeye3::output::glas::*;
-
-
-pub fn main() {
-    {
-        let logconf = simplelog::ConfigBuilder::new()
-            .set_thread_level(simplelog::LevelFilter::Error)
-            .set_thread_mode(simplelog::ThreadLogMode::Names)
-            .build();
-
-        simplelog::CombinedLogger::init(vec![simplelog::TermLogger::new(
-            //simplelog::LevelFilter::Error,
-            //simplelog::LevelFilter::Warn,
-            simplelog::LevelFilter::Info,
-            logconf,
-            simplelog::TerminalMode::Stderr,
-            simplelog::ColorChoice::Auto,
-        )])
-        .unwrap();
-    }
-
-    info!("Constructing i.MX8 decoding net...");
-    let use_random_xrdc_assignment = false;
-    let (dn, _imx8) = imx8::instantiate_imx8_example(use_random_xrdc_assignment).expect("could not create imx8 spec");
-    let dn = glas::DecodingNet::from_spec(&dn).expect("Could not create GLAS");
-
-    let mut ctxts = dn.contexts.clone();
-    let resources = dn.resources.clone();
-
-    let ctxts_to_remove = vec![
-        //"DSP_VIEW",
-        //"CM4 M4 core view",
-        "vas (MMU_DB_A35_0)",
-        "MMU_DB_A35_1",
-        "MMU_DB_A35_2",
-        "MMU_DB_A35_3",
-    ];
-    for ctx in ctxts_to_remove {
-        let id = dn.id_for(ctx);
-        let pos = ctxts.iter().position(|&x| x == id).unwrap();
-        ctxts.remove(pos);
-    }
-
-    let f = dn.flatten(&ctxts);
-    debug_flattened_address_spaces(&f, &dn);
-
-    eprintln!("contexts ({}): ", ctxts.len());
-    for &c in &ctxts {
-        eprint!(" '{}' ", dn.names[c]);
-    }
-    eprintln!();
-
-    eprintln!("resources ({}): ", resources.len());
-    for &r in &resources {
-        eprint!(" '{}' ", dn.names[r]);
-    }
-    eprintln!();
-    eprintln!();
-
-    debug_hw_matrix(&dn, &f, &ctxts, &resources);
-
-    eprintln!("deriving glas (w/Z3)...");
-    let glas = glas::derive_glas(&dn, &ctxts, &resources, &BTreeSet::new());
-    match &glas {
-        Ok(glas) => {
-            debug_glas(&glas, &dn, &ctxts, &resources);
-            let mut file = std::fs::File::create("/tmp/dn.gv").unwrap();
-            visualize_dn(&mut file, &dn).unwrap();
-            eprintln!("wrote dn dot to /tmp/dn.gv");
-            let mut file = std::fs::File::create("/tmp/glas.gv").unwrap();
-            visualize_glas(&mut file, &glas, &dn).unwrap();
-            eprintln!("wrote GLAS dot to /tmp/glas.gv");
-
-            let id = dn.names.iter().position(|p| p == "DB_A35_0").unwrap();
-            let mut file = std::fs::File::create("/tmp/glas_for.gv").unwrap();
-            visualize_glas_for(&mut file, &glas, &dn, &f, id, &resources).unwrap();
-            eprintln!("wrote GLAS dot of an A35 core to /tmp/glas_for.gv");
-
-            let mut file = File::create("/tmp/imx8_glas.json").unwrap();
-            output::glas::json(&mut file, &glas, &dn, &f, &resources).unwrap();
-            eprintln!("wrote json to /tmp/imx8_glas.json");
-        },
-        Err(err) => print_err(&err, &dn),
-    }
-
-}
-
-fn print_err(err: &DerivationError, dn: &DecodingNet) {
-    match err {
-        DerivationError::PlaceAlreadyTaken(res1, placed, res2, target) => {
-            eprintln!(
-                "to be placed resource '{}' conflicts already placed '{}'",
-                dn.names[*res2], dn.names[*res1],
-            );
-            eprintln!(" placed: {:#12x}, {} B", placed.offset, placed.length);
-            eprintln!(" target: {:#12x}, {} B", target.offset, target.length);
-        }
-        _ => unimplemented!(),
-    }
-}
-
-fn debug_hw_matrix(dn: &DecodingNet, f: &FlattenResult, ctxts: &Vec<usize>, resources: &Vec<usize>) {
-    let mut matrix = Vec::new();
-    let mut ress = BTreeMap::new();
-    for (_, space) in f.r.iter() {
-        for (_,r,rr) in space {
-            if !resources.contains(r) { continue; }
-            let e = ress.entry(r).or_insert(BTreeSet::new());
-            e.insert((rr.offset,rr.length));
-        }
-    }
-    let mut rs = Vec::new();
-    for (&r,rss) in ress.iter() {
-        for (ro,rl) in rss {
-            rs.push((r,ro,rl));
-        }
-    }
-
-    for (_, space) in f.r.iter() {
-        let mut row = vec![false; rs.len()];
-        for &(_, r, rr) in space {
-            match rs.iter().position(|&(&r1,&ro1,&rl1)| r1 == r && ro1 == rr.offset && rl1 == rr.length) {
-                Some(j) => row[j] = true,
-                None => continue,
-            }
-        }
-        matrix.push(row);
-    }
-
-    eprintln!("hardware protection matrix");
-
-    for _ in rs.iter() {
-        eprint!("  ");
-    }
-    eprintln!("  contexts");
-
-    for _ in rs.iter() {
-        eprint!("  ");
-    }
-    eprintln!("  --------");
-
-    for (i, row) in matrix.into_iter().enumerate() {
-        for col in row {
-            if col {
-                eprint!("x ");
-            } else {
-                eprint!("  ");
-            }
-        }
-        eprint!("| ");
-        let ctxidx = ctxts[i];
-        eprintln!("{}", dn.names[ctxidx]);
-    }
-
-    for _ in rs.iter() {
-        eprint!("--")
-    }
-    eprintln!("+");
-
-    let mut i = 0;
-    let mut do_continue = true;
-    while do_continue {
-        do_continue = false;
-        for &(r,ro,_) in rs.iter() {
-            let s = format!("{} @ {:#x}", &dn.names[*r], ro);
-            if i < s.len() {
-                do_continue = true;
-                eprint!("{} ", s.bytes().nth(i).unwrap() as char);
-            } else {
-                eprint!("  ");
-            }
-        }
-
-        eprint!("| ");
-
-        if i < "resources".len() {
-            do_continue = true;
-            eprint!("{} ", "resources".bytes().nth(i).unwrap() as char);
-        } else {
-            eprint!("  ");
-        }
-
-
-        i += 1;
-        eprintln!();
-    }
-
-}
diff --git a/src/bin/repl.rs b/src/bin/repl.rs
index 2fe24261d551f7096d70c188fc8b2769b6fa687d..0327bc49f72baa44db4e98d3b96f4108b0cb701f 100644
--- a/src/bin/repl.rs
+++ b/src/bin/repl.rs
@@ -2,24 +2,29 @@ use sockeye3::glas;
 use sockeye3::glas::GLAS;
 use sockeye3::output;
 use sockeye3::output::human_bytes;
+use sockeye3::range::Range;
 use sockeye3::spec;
 use sockeye3::specs::examples;
 
 use std::collections::{BTreeMap,BTreeSet};
+use std::env;
 use std::io;
 use std::io::Write;
 
+use serde::{Serialize,Deserialize};
+
 type SpecDef = (String, fn(&Configuration) -> Result<spec::DecodingNet, spec::SpecError>);
 
 struct State {
     specs: BTreeMap<String, SpecDef>,
+    glas_constraints: Vec<(usize, u128, u128, u128)>,
     glas: Option<(glas::DecodingNet, GLAS)>,
     cfg: Configuration,
     chosen: String,
     model: spec::DecodingNet,
 }
 
-fn main() -> Result<(), ReplError>  {
+fn main() -> Result<(), ReplError> {
     let mut specs: BTreeMap<String, SpecDef> = BTreeMap::new();
     specs.insert("empty".to_string(), (
         "An empty decoding net".to_string(),
@@ -29,6 +34,10 @@ fn main() -> Result<(), ReplError>  {
         "A model of the NXP i.MX8".to_string(),
         |cfg: &Configuration| examples::imx8::instantiate_imx8_example(cfg.imx8.use_random_xrdc).map(|s| s.0),
     ));
+    specs.insert("morello".to_string(), (
+        "A model of the Morello FVP".to_string(),
+        |_| examples::morello::instantiate_morello_example().map(|s| s.0),
+    ));
     specs.insert("qemu_aarch64".to_string(), (
         "A model of QEMU's aarch64 virt platform".to_string(),
         |_| examples::qemu_aarch64::create_dn(),
@@ -40,12 +49,28 @@ fn main() -> Result<(), ReplError>  {
 
     let mut state = State {
         specs,
+        glas_constraints: Vec::new(),
         glas: None,
         cfg,
         chosen,
         model,
     };
 
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 {
+        let path = &args[1];
+        println!("intepreting file '{}'", path);
+        let file = std::fs::File::open(path)?;
+        let mut r = std::io::BufReader::new(file);
+        state.run(&mut r, false)?;
+    } else {
+        repl(&mut state)?;
+    }
+
+    Ok(())
+}
+
+fn repl(state: &mut State) -> Result<(), ReplError>  {
     let stdin = io::stdin();
     let mut handle = stdin.lock();
 
@@ -57,27 +82,35 @@ fn main() -> Result<(), ReplError>  {
 impl State {
     fn run(&mut self, handle: &mut impl io::BufRead, interactive: bool) -> Result<(), ReplError>  {
         let mut line = String::new();
-        let mut cmds = vec![
-            ("add_address_space", "name offset len", "Add a new address space"),
-            ("add_mapping",       "src dst src_off dst_off len", "Add a new mapping"),
-            ("exit",              "", "Exit the REPL"),
-            ("derive_glas",       "", "Derive the GLAS"),
-            ("glas",              "", "Print the GLAS"),
-            ("glas_for",          "name", "Print the GLAS for a specific core"),
-            ("glas_json",         "path", "Write the GLAS description as JSON to path"),
-            ("help",              "", "Print available commands"),
-            ("info",              "", "Print info about the current model"),
-            ("mark_context",      "name", "Mark an address space as a context"),
-            ("mark_resource",     "name", "Mark an address space as a resource"),
-            ("models",            "", "List available models"),
-            ("quit",              "", "Quit the REPL"),
-            ("read_file",         "path", "Read and execute the commands in from a file"),
-            ("set_model",         "name", "Set the current model"),
-            ("viz_dn",            "path [open]", "Visualize the decoding net using graphviz"),
-            ("viz_glas",          "path [open]", "Visualize the GLAS using graphviz"),
-            ("viz_glas_for",      "name path [open]", "Visualize the GLAS for a specific core using graphviz"),
-        ];
-        cmds.sort();
+        let cmds: BTreeMap<&str, (&str, &str)> = BTreeMap::from([
+            ("add_address_space",   ("name offset len", "Add a new address space")),
+            ("add_mapping",         ("src dst src_off dst_off len", "Add a new mapping")),
+            ("add_glas_constraint", ("name phys_addr glas_addr len",
+                                     "Add a constraint to the GLAS to force (phys_addr, len) in name to map to (glas_addr, len)")),
+            ("as_info",             ("name", "Print info about given address_space")),
+            ("exit",                ("", "Exit the REPL")),
+            ("derive_glas",         ("", "Derive the GLAS")),
+            ("flattened_context",   ("name", "Print details of flattened context")),
+            ("gen_pt",              ("name base_addr path", "Generate page tables as C header file for the given context")),
+            ("glas",                ("", "Print the GLAS")),
+            ("glas_for",            ("name", "Print the GLAS for a specific core")),
+            ("glas_json",           ("path", "Write the GLAS description as JSON to path")),
+            ("hw_matrix",           ("[path]", "Output the hardware protection matrix")),
+            ("help",                ("", "Print available commands")),
+            ("info",                ("", "Print info about the current model")),
+            ("load_glas",           ("path", "Load a derived GLAS from a file")),
+            ("mark_context",        ("name", "Mark an address space as a context")),
+            ("mark_resource",       ("name", "Mark an address space as a resource")),
+            ("models",              ("", "List available models")),
+            ("quit",                ("", "Quit the REPL")),
+            ("read_file",           ("path", "Read and execute the commands in from a file")),
+            ("save_glas",           ("path", "Save the derived GLAS to a file")),
+            ("set_model",           ("name", "Set the current model")),
+            ("trace_path",          ("name addr", "Trace address decoding path of given address")),
+            ("viz_dn",              ("path [open]", "Visualize the decoding net using graphviz")),
+            ("viz_glas",            ("path [open]", "Visualize the GLAS using graphviz")),
+            ("viz_glas_for",        ("name path [open]", "Visualize the GLAS for a specific core using graphviz")),
+        ]);
         loop {
             line.clear();
             if interactive { print!("> "); }
@@ -90,16 +123,24 @@ impl State {
             let res = match cmd {
                 Some("add_address_space") => self.add_address_space(&mut args),
                 Some("add_mapping") => self.add_mapping(&mut args),
+                Some("add_glas_constraint") => self.add_glas_constraint(&mut args),
+                Some("as_info") => self.print_address_space_info(&mut args),
                 Some("derive_glas") => self.derive_glas(),
+                Some("gen_pt") => self.generate_page_table_for_context(&mut args),
                 Some("glas") => self.print_glas(),
                 Some("glas_for") => self.print_glas_from_view(&mut args),
                 Some("glas_json") => self.glas_json(&mut args),
+                Some("hw_matrix") => self.print_hardware_matrix(&mut args),
+                Some("flattened_context") => self.print_flattened_context(&mut args),
                 Some("info") => self.print_info(),
+                Some("load_glas") => self.load_glas(&mut args),
                 Some("mark_context") => self.mark_context(&mut args),
                 Some("mark_resource") => self.mark_resource(&mut args),
                 Some("models") => self.list_models(),
+                Some("save_glas") => self.save_glas(&mut args),
                 Some("set_model") => self.set_model(&mut args),
                 Some("read_file") => self.read_file(&mut args),
+                Some("trace_path") => self.trace_path(&mut args),
                 Some("viz_dn") => self.visualize_dn(&mut args),
                 Some("viz_glas") => self.visualize_glas(&mut args),
                 Some("viz_glas_for") => self.visualize_glas_for(&mut args),
@@ -107,7 +148,7 @@ impl State {
                 Some("exit") => break,
                 Some("help") => {
                     println!("{:20} {:30} Description", "Command", "Parameters");
-                    for (name, params, descr) in cmds.iter() {
+                    for (name, (params, descr)) in cmds.iter() {
                         println!("{name:20} {params:30} {descr}");
                     }
                     Ok(())
@@ -116,12 +157,26 @@ impl State {
                 None => Err(ReplError::NoArgumentProvided),
             };
 
-            let e = match res {
-                Ok(()) => continue,
-                Err(e) => e,
-            };
-
-            println!("error: {e}");
+            match res {
+                Ok(()) => (),
+                Err(e) => {
+                    println!("{}", e);
+                    let cmd = cmd.unwrap_or("");
+                    match cmds.get(cmd) {
+                        Some((params, descr)) => {
+                            println!("usage:");
+                            println!("{cmd} {params} -- {descr}")
+                        },
+                        None => {
+                            println!("commands:");
+                            println!("{:20} {:30} Description", "Command", "Parameters");
+                            for (name, (params, descr)) in cmds.iter() {
+                                println!("{name:20} {params:30} {descr}");
+                            }
+                        }
+                    }
+                },
+            }
         }
 
         Ok(())
@@ -143,6 +198,7 @@ impl State {
         println!("setting model '{}'", m);
         self.model = f(&self.cfg)?;
         self.chosen = m.to_string();
+        self.glas_constraints.clear();
         self.glas = None;
 
         Ok(())
@@ -161,6 +217,207 @@ impl State {
         Ok(())
     }
 
+    fn print_hardware_matrix<'a>(&self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+       let mut handle: Box<dyn io::Write> = match Self::next_arg(args) {
+           Ok(path) => Box::new(std::fs::File::create(path)?),
+           Err(_) => Box::new(std::io::stdout().lock()),
+       };
+
+       let dn = glas::DecodingNet::from_spec(&self.model).unwrap();
+       let f = dn.flatten(&dn.contexts);
+
+       let ctxts = &dn.contexts;
+       let resources = &dn.resources;
+
+       let mut matrix = Vec::new();
+       let mut ress = BTreeMap::new();
+       for (_, space) in f.r.iter() {
+           for (_,r,rr) in space {
+               if !resources.contains(r) { continue; }
+               let e = ress.entry(r).or_insert(BTreeSet::new());
+               e.insert((rr.offset,rr.length));
+           }
+       }
+       let mut rs = Vec::new();
+       for (&r,rss) in ress.iter() {
+           for (ro,rl) in rss {
+               rs.push((r,ro,rl));
+           }
+       }
+
+       for (_, space) in f.r.iter() {
+           let mut row = vec![false; rs.len()];
+           for &(_, r, rr) in space {
+               match rs.iter().position(|&(&r1,&ro1,&rl1)| r1 == r && ro1 == rr.offset && rl1 == rr.length) {
+                   Some(j) => row[j] = true,
+                   None => continue,
+               }
+           }
+           matrix.push(row);
+       }
+
+       writeln!(handle, "hardware protection matrix for model '{}'", self.chosen)?;
+
+       for _ in rs.iter() {
+           write!(handle, "  ")?;
+       }
+       writeln!(handle, "  contexts")?;
+
+       for _ in rs.iter() {
+           write!(handle, "  ")?;
+       }
+       writeln!(handle, "  --------")?;
+
+       for (i, row) in matrix.into_iter().enumerate() {
+           for col in row {
+               if col {
+                   write!(handle, "x ")?;
+               } else {
+                   write!(handle, "  ")?;
+               }
+           }
+           write!(handle, "| ")?;
+           let ctxidx = ctxts[i];
+           writeln!(handle, "{}", dn.names[ctxidx])?;
+       }
+
+       for _ in rs.iter() {
+           write!(handle, "--")?;
+       }
+       writeln!(handle, "+")?;
+
+       let mut i = 0;
+       let mut do_continue = true;
+       while do_continue {
+           do_continue = false;
+           for &(r,ro,_) in rs.iter() {
+               let s = format!("{} @ {:#x}", &dn.names[*r], ro);
+               if i < s.len() {
+                   do_continue = true;
+                   write!(handle, "{} ", s.bytes().nth(i).unwrap() as char)?;
+               } else {
+                   write!(handle, "  ")?;
+               }
+           }
+
+           write!(handle, "| ")?;
+
+           if i < "resources".len() {
+               do_continue = true;
+               write!(handle, "{} ", "resources".bytes().nth(i).unwrap() as char)?;
+           } else {
+               write!(handle, "  ")?;
+           }
+
+
+           i += 1;
+           writeln!(handle)?;
+       }
+
+        Ok(())
+    }
+
+    fn print_address_space_info<'a>(&self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        let (name, asid) = Self::next_asid(&self.model, args)?;
+        let address_space = &self.model.address_spaces[asid.as_usize()];
+
+        println!("context {}", name);
+        println!("description: {}", address_space.description);
+        println!(
+            "addresses {:#x} - {:#x}, length {}",
+            address_space.range.offset,
+            address_space.range.offset + address_space.range.length - 1,
+            human_bytes(address_space.range.length),
+        );
+
+        let mappings = &self.model.mappings[asid.as_usize()];
+        if mappings.is_empty() {
+            println!("no mappings");
+        } else {
+            println!("mappings");
+            for mapping in self.model.mappings[asid.as_usize()].iter() {
+                let dst = &self.model.address_spaces[mapping.dst.as_usize()].name;
+                println!(
+                    "{:#20x} - {:#20x} maps to {:20} @ {:#20x}   {}",
+                    address_space.range.offset,
+                    address_space.range.offset + address_space.range.length - 1,
+                    dst,
+                    mapping.dst_range.offset,
+                    mapping.description,
+                );
+            }
+        }
+
+        Ok(())
+    }
+
+    fn print_flattened_context<'a>(&self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        let (name, asid) = Self::next_asid(&self.model, args)?;
+        self.model.contexts.contains(&asid).then_some(asid).ok_or(ReplError::NotAContext(name.to_string()))?;
+
+        let spec = glas::DecodingNet::from_spec(&self.model).unwrap();
+        let f = spec.flatten(&spec.contexts);
+        println!("flattened context {}", name);
+
+        for &(src_range, r, dst_range) in f.r[&asid.as_usize()].iter() {
+            let r_name = &self.model.address_spaces[r].name;
+            println!(
+                "{:#20x} - {:#20x} maps to {:20} @ {:#20x}",
+                src_range.offset,
+                src_range.offset + src_range.length - 1,
+                r_name,
+                dst_range.offset
+            );
+        }
+
+        Ok(())
+    }
+
+    fn trace_path<'a>(&self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        let (name, asid) = Self::next_asid(&self.model, args)?;
+        let addr = Self::next_num(args)?;
+
+        println!("tracing decoding of address {:#x} in address space {}", addr, name);
+
+        let mut a = asid;
+        let mut addr = addr;
+        loop {
+            let space = &self.model.address_spaces[a.as_usize()];
+            if addr < space.range.offset || addr >= space.range.offset + space.range.length {
+                return Err(ReplError::AddressOutOfRange(addr, space.range));
+            }
+
+
+            let current_mappings = &self.model.mappings[a.as_usize()];
+            if current_mappings.len() == 0 { break; }
+
+            let idx = current_mappings.binary_search_by_key(
+                &addr,
+                |m| m.src_range.offset + m.src_range.length - 1
+            );
+
+            let idx = match idx {
+                Ok(idx) => idx,
+                Err(idx) => idx,
+            };
+
+            assert!(idx < current_mappings.len());
+            let mapping = &current_mappings[idx];
+            println!("{:#20x} in {:10}   -- {}", addr, space.name, mapping.description);
+
+            if mapping.src_range.offset <= addr && addr < mapping.src_range.offset + mapping.src_range.length {
+                let diff = mapping.dst_range.offset as i128 - mapping.src_range.offset as i128;
+                addr = (addr as i128 + diff) as u128;
+                a = mapping.dst;
+            } else {
+                break;
+            }
+        }
+
+        println!("{:#20x} in {:10}", addr, self.model.address_spaces[a.as_usize()].name);
+        Ok(())
+    }
+
     fn print_info(&self) -> Result<(), ReplError> {
         println!("selected model {}", self.chosen);
         println!("number of address spaces: {}", self.model.address_spaces.len());
@@ -182,7 +439,7 @@ impl State {
 
     fn derive_glas(&mut self) -> Result<(), ReplError> {
         let spec = glas::DecodingNet::from_spec(&self.model).unwrap();
-        let glas = glas::derive_glas(&spec, &spec.contexts, &spec.resources, &BTreeSet::new()).unwrap();
+        let glas = glas::derive_glas(&spec, &spec.contexts, &spec.resources, &self.glas_constraints).unwrap();
         self.glas = Some((spec, glas));
         Ok(())
     }
@@ -236,11 +493,11 @@ impl State {
             Ok("open") => {
                 println!("compiling figure...");
                 let status = std::process::Command::new("dot")
-                    .args(["-Tpng", "-o", "/tmp/img.png", path])
+                    .args(["-Tpdf", "-o", "/tmp/figure.pdf", path])
                     .status()?;
                 if !status.success() { return Err(ReplError::UnknownError) }
                 println!("opening figure");
-                open::that("/tmp/img.png")?;
+                open::that("/tmp/figure.pdf")?;
             },
             Ok(cmd) => return Err(ReplError::UnknownCommand(cmd.to_string())),
             Err(_) => {},
@@ -302,14 +559,90 @@ impl State {
         Ok(())
     }
 
+    fn generate_page_table_for_context<'a>(&mut self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        match &self.glas {
+            None => self.derive_glas()?,
+            _ => (),
+        }
+        let (spec, glas) = self.glas.as_ref().unwrap();
+        let f = spec.flatten(&spec.contexts);
+
+        let (name, asid) = Self::next_asid(&self.model, args)?;
+        self.model.contexts.contains(&asid).then_some(()).ok_or(ReplError::NotAContext(name.clone()))?;
+
+        let base_addr = Self::next_num(args)?;
+
+        let path = Self::next_arg(args)?;
+        let mut file = std::fs::File::create(path)?;
+
+        eprintln!("deriving page table...");
+        let pt = glas::page_table_from_glas(
+            &glas,
+            &f,
+            asid.as_usize(),
+            base_addr,
+            &spec.resources,
+        );
+
+        eprintln!("number of pages used for page table: {} ({})", pt.page_tracker.used_pages, output::human_bytes(pt.page_tracker.used_pages as u128 * 4096));
+        let num_entries = pt.page_tracker.used_pages * 512;
+        let mut entries = vec![0; num_entries];
+        pt.base_table.materialize_into(&mut entries);
+
+        writeln!(file, "typedef unsigned long long pt_entry;")?;
+        writeln!(file, "unsigned long long num_page_table_entries = {:#x};", num_entries)?;
+        writeln!(file, "pt_entry *page_table_base = (pt_entry *)({:#x});", base_addr)?;
+        writeln!(file, "pt_entry page_table_entries[{}] = {{", num_entries)?;
+
+        let mut addr = base_addr;
+        for chunk in entries.chunks(8) {
+            if addr % 4096 == 0 {
+                writeln!(file, "  // ----------- {:#x} -------------", addr)?;
+            }
+            write!(file, " ")?;
+            for entry in chunk {
+                write!(file, " {:#x},", entry)?;
+                addr += 8;
+            }
+            writeln!(file)?;
+        }
+        writeln!(file, "}};")?;
+
+        writeln!(file, r#"
+void activate_page_tables() {{
+    for (int i = 0; i < num_page_table_entries; i++) {{
+        page_table_base[i] = page_table_entries[i];
+    }}
+    __asm__ ("LDR     x1, ={:#x}\n\t"   // program ttbr0 on this CPU
+             "MSR     ttbr0_el1, x1\n\t"
+             "LDR     x1, =0xff\n\t"         // program mair on this CPU
+             "MSR     mair_el1, x1\n\t"
+             "LDR     x1, =0x500803510\n\t"  // program tcr on this CPU
+             "MSR     tcr_el1, x1\n\t"
+             "ISB\n\t"
+             "MRS     x2, tcr_el1\n\t"       // verify CPU supports desired config
+             "CMP     x2, x1\n\t"
+             "B.NE    .\n\t"
+             "LDR     x1, =0x1005\n\t"       // program sctlr on this CPU
+             "MSR     sctlr_el1, x1\n\t"
+             "ISB\n\t"                       // synchronize context on this CPU
+             : : :
+    );
+}}
+"#,
+        base_addr)?;
+        println!("wrote page tables to '{}'", path);
+        Ok(())
+    }
+
     fn next_arg<'a>(args: &mut impl Iterator<Item = &'a str>) -> Result<&'a str, ReplError> {
         args.next().ok_or(ReplError::NoArgumentProvided)
     }
 
-    fn next_num<'a, T>(args: &mut impl Iterator<Item = &'a str>) -> Result<T, ReplError>
-      where T: std::str::FromStr {
+    fn next_num<'a>(args: &mut impl Iterator<Item = &'a str>) -> Result<u128, ReplError> {
         let s = args.next().ok_or(ReplError::NoArgumentProvided)?;
-        let t = s.parse::<T>().map_err(|_| ReplError::NotANumber(s.to_string()))?;
+        let s = s.strip_prefix("0x").ok_or(ReplError::InvalidNumberFormat(s.to_string()))?.replace("_", "");
+        let t = u128::from_str_radix(&s, 16).map_err(|_| ReplError::NotANumber(s.to_string()))?;
         Ok(t)
     }
 
@@ -323,8 +656,8 @@ impl State {
         let name = args.next().ok_or(ReplError::NoArgumentProvided)?;
         //let asid = self.model.address_spaces.iter().position(|a| a.name == name).ok_or(ReplError::NotAnAddressSpace(name.to_string()))?;
 
-        let offset = Self::next_num::<u128>(args)?;
-        let len = Self::next_num::<u128>(args)?;
+        let offset = Self::next_num(args)?;
+        let len = Self::next_num(args)?;
 
         let _ = self.model.add_address_space(name, "", offset, len)?;
         self.glas = None;
@@ -337,9 +670,9 @@ impl State {
         let (src, src_asid) = Self::next_asid(&self.model, args)?;
         let (dst, dst_asid) = Self::next_asid(&self.model, args)?;
 
-        let src_offset = Self::next_num::<u128>(args)?;
-        let dst_offset = Self::next_num::<u128>(args)?;
-        let len = Self::next_num::<u128>(args)?;
+        let src_offset = Self::next_num(args)?;
+        let dst_offset = Self::next_num(args)?;
+        let len = Self::next_num(args)?;
 
         let _ = self.model.add_mapping(src_asid, dst_asid, src_offset, dst_offset, len, "")?;
         self.glas = None;
@@ -348,6 +681,60 @@ impl State {
         Ok(())
     }
 
+    fn add_glas_constraint<'a>(&mut self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        let (name, asid) = Self::next_asid(&self.model, args)?;
+        let phys_addr = Self::next_num(args)?;
+        let glas_addr = Self::next_num(args)?;
+        let len = Self::next_num(args)?;
+
+        let spec = glas::DecodingNet::from_spec(&self.model).unwrap();
+        let f = spec.flatten(&spec.contexts);
+        let mut is_valid_additional_constraint = false;
+        for &(src_range, _, _) in f.r[&asid.as_usize()].iter() {
+            if src_range.offset == phys_addr && src_range.length == len {
+                is_valid_additional_constraint = true;
+            }
+        }
+        if !is_valid_additional_constraint {
+            return Err(ReplError::InvalidGlasConstraint(phys_addr, glas_addr, len));
+        }
+
+        self.glas = None;
+        self.glas_constraints.push((asid.as_usize(), phys_addr, glas_addr, len));
+        println!("added constraint to map {}@{:#x} to {:#x} in the GLAS", name, phys_addr, glas_addr);
+
+        Ok(())
+    }
+
+    fn load_glas<'a>(&mut self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        let path = Self::next_arg(args)?;
+        let file = std::fs::File::open(path)?;
+
+        let loaded_glas: SerializedGlas = serde_json::from_reader(file).map_err(|_| ReplError::InvalidGlas(path.to_string()))?;
+
+        let expected_spec = glas::DecodingNet::from_spec(&self.model).unwrap();
+        if loaded_glas.spec != expected_spec {
+            return Err(ReplError::InvalidGlas(path.to_string()));
+        }
+
+        self.glas = Some((loaded_glas.spec, loaded_glas.glas));
+        Ok(())
+    }
+
+    fn save_glas<'a>(&mut self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
+        let path = Self::next_arg(args)?;
+        let file = std::fs::File::create(path)?;
+
+        match &self.glas {
+            None => self.derive_glas()?,
+            _ => (),
+        }
+        let (spec, glas) = self.glas.as_ref().unwrap();
+        let sg = SerializedGlas{ glas: glas.clone(), spec: spec.clone() };
+        serde_json::to_writer(file, &sg).map_err(|_| ReplError::UnknownError)?;
+        Ok(())
+    }
+
     fn mark_context<'a>(&mut self, args: &mut impl Iterator<Item = &'a str>) -> Result<(), ReplError> {
         let (name, asid) = Self::next_asid(&self.model, args)?;
         self.model.mark_context(asid, "");
@@ -367,6 +754,12 @@ impl State {
     }
 }
 
+#[derive(Serialize, Deserialize)]
+struct SerializedGlas {
+    spec: glas::DecodingNet,
+    glas: GLAS,
+}
+
 #[derive(Debug)]
 #[allow(dead_code)]
 enum ReplError {
@@ -379,6 +772,10 @@ enum ReplError {
     NotANumber(String),
     NoArgumentProvided,
     SpecError(spec::SpecError),
+    AddressOutOfRange(u128, Range),
+    InvalidGlasConstraint(u128, u128, u128),
+    InvalidNumberFormat(String),
+    InvalidGlas(String),
     UnknownError,
 }
 
@@ -393,7 +790,11 @@ impl std::fmt::Display for ReplError {
             ReplError::UnknownCommand(c) => write!(f, "unknown command '{}'", c),
             ReplError::NotANumber(n) => write!(f, "not a number: '{}'", n),
             ReplError::NoArgumentProvided => write!(f, "missing argument"),
+            ReplError::AddressOutOfRange(a, r) => write!(f, "{:#x} is not in range {:#x} - {:#x}", a, r.offset, r.offset + r.length - 1),
             ReplError::UnknownError => write!(f, "unknown error"),
+            ReplError::InvalidGlasConstraint(_pa, _ga, _l) => write!(f, "invalid glas constraint, can only map full resources for now\n  see 'flatten_context' for the ranges that can be flattened"),
+            ReplError::InvalidNumberFormat(s) => write!(f, "invalid number format '{}': hex prefix (0x) required", s),
+            ReplError::InvalidGlas(p) => write!(f, "invalid glas at path '{}'", p),
             ReplError::SpecError(e) => write!(f, "underlying spec error: {:?}", e),
         }?;
 
diff --git a/src/glas.rs b/src/glas.rs
index 76a47664da94907e98cdb4d4a4589fb767fa5391..fc01c5be169eb2625193ea2549d54aafb19918c2 100644
--- a/src/glas.rs
+++ b/src/glas.rs
@@ -10,12 +10,14 @@
 
 use std::collections::BTreeMap;
 use std::collections::BTreeSet;
+use std::io::Write;
 
 use crate::range::Range;
 use crate::range_tree::RangeTree;
 use crate::spec;
+use crate::specs::examples::mmu::PageTable;
 
-use std::io::Write;
+use serde::{Serialize, Deserialize};
 
 pub type ASID = usize;
 
@@ -50,7 +52,7 @@ pub fn derive_glas(
     dn: &DecodingNet,
     contexts: &Vec<usize>,
     resources: &Vec<usize>,
-    _required: &BTreeSet<(usize, usize)>,
+    additional_constraints: &Vec<(usize, u128, u128, u128)>,
 ) -> Result<GLAS, DerivationError> {
     let mut glas = GLAS::new();
 
@@ -58,7 +60,7 @@ pub fn derive_glas(
     let ctx = z3::Context::new(&cfg);
 
     let mut buf = Vec::new();
-    let (loffs, goffs) = write_smtlib_program_to(dn, contexts, resources, _required, &mut buf)?;
+    let (loffs, goffs) = write_smtlib_program_to(dn, contexts, resources, additional_constraints, &mut buf)?;
     let prog = String::from_utf8(buf).unwrap();
 
     eprintln!("initializing solver");
@@ -118,6 +120,7 @@ pub fn derive_glas(
     }
 
     glas.placement.sort_by_key(|b| b.1.offset);
+    glas.swizzles.sort_by_key(|b| (b.0, b.1.offset));
     Ok(glas)
 }
 
@@ -125,7 +128,7 @@ fn write_smtlib_program_to<W: Write>(
     dn: &DecodingNet,
     contexts: &Vec<usize>,
     resources: &Vec<usize>,
-    _required: &BTreeSet<(usize, usize)>,
+    additional_constraints: &Vec<(usize, u128, u128, u128)>,
     w: &mut W,
 ) -> Result<
     (
@@ -193,6 +196,25 @@ fn write_smtlib_program_to<W: Write>(
     }
     eprintln!("wrote constraints for local-to-global offsets");
 
+    for &(name, phys_addr, glas_addr, len) in additional_constraints {
+        // we want phys_addr, len to map to glas_addr, len
+        // TODO: hack: we disallow any cases where (phys_addr, len) is not the length of an existing
+        // resource. this can be fixed easily by taking additional_constraints into consideration
+        // during glas::DecodingNet.
+        let mut is_valid_additional_constraint = false;
+        for &(src_range, _, _) in f[&name].iter() {
+            if src_range.offset == phys_addr && src_range.length == len {
+                is_valid_additional_constraint = true;
+            }
+        }
+        assert!(is_valid_additional_constraint);
+
+        let o = format!("o_{}_{}", name, phys_addr);
+        let o_value: i128 = glas_addr as i128 - phys_addr as i128;
+        writeln!(w, "(assert (= {o} {o_value}))")?;
+    }
+    eprintln!("declared additional constriants");
+
     eprintln!(
         "writing disjointness constraints num={}",
         vars.keys().count()
@@ -264,7 +286,7 @@ pub fn debug_glas(glas: &GLAS, dn: &DecodingNet, ctxts: &Vec<usize>, res: &Vec<u
 /// A decoding net, in a representation useful GLAS derivation.
 ///
 /// It is constructed from a general decoding net.
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
 pub struct DecodingNet {
     pub address_spaces: Vec<RangeTree<(usize, Range)>>,
     pub names: Vec<String>,
@@ -481,7 +503,7 @@ impl DecodingNet {
 }
 
 /// Represents a global logical address space, computed for a particular decoding net.
-#[derive(Debug)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct GLAS {
     /// Records the placement of a particular resource range in a contiguous (offset, length)
     /// region. For more efficient range lookups, we should use something akin to an interval tree.
@@ -500,6 +522,53 @@ impl GLAS {
     }
 }
 
+/// Generate 4-level page tables from this GLAS for this context.
+///
+/// TODO: this should probably be a function per-mmu
+pub fn page_table_from_glas(glas: &GLAS, f: &FlattenResult, context: usize, base_addr: u128, resources: &Vec<usize>) -> PageTable {
+    let mut page_table = PageTable::new(base_addr);
+
+    let space = &f.r[&context];
+    let mut placed = vec![false; glas.placement.len()];
+    for (ctx_range, resource, resource_range) in space.iter().filter(|(_,r,_)| resources.contains(r)) {
+        let i = glas.placement.iter().position(|(r,_,c)| r == resource && c == resource_range).unwrap();
+        let (_, glas_range, _) = glas.placement[i];
+
+        if placed[i] { continue; }
+
+        assert!(ctx_range.offset % 4096 == 0);
+        assert!(glas_range.offset % 4096 == 0);
+
+        assert!(ctx_range.length % 4096 == 0);
+        assert!(ctx_range.length == glas_range.length);
+
+
+        let sz = if ctx_range.offset % (1 << 30) == 0 && glas_range.offset % (1 << 30) == 0 && glas_range.length % (1 << 30) == 0 {
+            1 << 30
+        } else if ctx_range.offset % (1 << 21) == 0 && glas_range.offset % (1 << 21) == 0 && glas_range.length % (1 << 21) == 0 {
+            1 << 21
+        } else {
+            1 << 12
+        };
+
+        placed[i] = true;
+        for i in 0..ctx_range.length / sz {
+            let nsz = (sz * i) as u64;
+            let src = glas_range.offset as u64 + nsz;
+            let dst = ctx_range.offset as u64 + nsz;
+            match sz {
+                1073741824 => page_table.add_1g_mapping(src, dst),
+                2097152 => page_table.add_2m_mapping(src, dst),
+                4096 => page_table.add_4k_mapping(src, dst),
+                _ => unreachable!(),
+            }
+        }
+
+    }
+
+    page_table
+}
+
 #[derive(Debug)]
 pub struct FlattenResult {
     pub r: BTreeMap<usize, Vec<(Range, usize, Range)>>,
@@ -521,8 +590,7 @@ fn test_fail_conflict() {
     dn.add_mapping(i, r1, 0, 0, 10).unwrap();
     dn.add_mapping(i, r2, 10, 0, 10).unwrap();
 
-    let required = BTreeSet::from_iter(vec![(c1, r1), (c1, r2), (c2, r1), (c2, r2)]);
-    let glas = derive_glas(&dn, &vec![c1, c2], &vec![r1, r2], &required).expect("can derive glas");
+    let glas = derive_glas(&dn, &vec![c1, c2], &vec![r1, r2], &Vec::new()).expect("can derive glas");
     _ = glas;
 }
 
diff --git a/src/output/glas.rs b/src/output/glas.rs
index 04d4519b88db3b9478ef2e95fd05d5623ac54f75..b0162731e719fe4e07d7edc3c3ce9769370fef97 100644
--- a/src/output/glas.rs
+++ b/src/output/glas.rs
@@ -15,7 +15,7 @@ pub fn json(file: &mut File, glas: &GLAS, dn: &DecodingNet, f: &FlattenResult, r
 
         let reslen = space.iter().filter(|(_,r,_)| resources.contains(r)).count();
         for (idx, (context_range,r,resource_range)) in space.iter().filter(|(_,r,_)| resources.contains(r)).enumerate() {
-            let i = glas.placement.iter().position(|(_,_,c)| c == resource_range).unwrap();
+            let i = glas.placement.iter().position(|(res,_,c)| r == res && c == resource_range).unwrap();
             let (_,glas_range,_) = glas.placement[i];
             write!(file, r#"
 {{
@@ -78,13 +78,15 @@ pub fn json(file: &mut File, glas: &GLAS, dn: &DecodingNet, f: &FlattenResult, r
 /// accepting components will be color-coded. The resulting file can be processed using the `dot`
 /// layout algorithm in the graphviz tools.
 pub fn visualize_dn(file: &mut File, dn: &DecodingNet) -> Result<(), std::io::Error> {
-    writeln!(file,"digraph {{")?;
-    writeln!(file,"  rankdir=\"LR\";")?;
+    writeln!(file, "digraph {{")?;
+    writeln!(file, "  rankdir=\"LR\";")?;
+    writeln!(file, "  ranksep=50;")?;
+    writeln!(file, "  nodesep=1;")?;
     writeln!(file,"  overlap=false;")?;
     //writeln!(file,"  graph [splines=ortho];");
-    writeln!(file,"  node [shape=record,style=filled,fontname = \"monospace\"];")?;
-    writeln!(file,"  label=\"The Whole Decoding Net\";")?;
-    writeln!(file,"  labelloc=t;")?;
+    writeln!(file, "  node [shape=record,style=filled,fontname = \"monospace\"];")?;
+    writeln!(file, "  label=\"The Whole Decoding Net\";")?;
+    writeln!(file, "  labelloc=t;")?;
 
     for (id, _address_space) in dn.address_spaces.iter().enumerate() {
         for map in dn.address_spaces[id].values.iter() {
@@ -120,13 +122,14 @@ pub fn visualize_dn(file: &mut File, dn: &DecodingNet) -> Result<(), std::io::Er
 }
 
 pub fn visualize_glas(file: &mut File, glas: &GLAS, dn: &DecodingNet) -> Result<(), std::io::Error> {
-    writeln!(file,"digraph {{")?;
-    writeln!(file,"  rankdir=\"LR\";")?;
-    writeln!(file,"  overlap=false;")?;
+    writeln!(file, "digraph {{")?;
+    writeln!(file, "  rankdir=\"LR\";")?;
+    writeln!(file, "  overlap=false;")?;
+    writeln!(file, "  ranksep=10;")?;
     //writeln!(file,"  graph [splines=ortho];")?;
-    writeln!(file,"  node [shape=record,style=filled,fontname = \"monospace\"];")?;
-    writeln!(file,"  label=\"GLAS for this SoC\";")?;
-    writeln!(file,"  labelloc=t;")?;
+    writeln!(file, "  node [shape=record,style=filled,fontname = \"monospace\"];")?;
+    writeln!(file, "  label=\"GLAS for this SoC\";")?;
+    writeln!(file, "  labelloc=t;")?;
 
     for (r,glas_range,res_range) in glas.placement.iter().rev() {
         writeln!(file,
@@ -165,13 +168,14 @@ pub fn visualize_glas_for(
     c: usize,
     resources: &Vec<usize>
 ) -> Result<(), std::io::Error> {
-    writeln!(file,"digraph {{")?;
-    writeln!(file,"  rankdir=\"LR\";")?;
-    writeln!(file,"  overlap=false;")?;
+    writeln!(file, "digraph {{")?;
+    writeln!(file, "  rankdir=\"LR\";")?;
+    writeln!(file, "  ranksep=15;")?;
+    writeln!(file, "  overlap=false;")?;
     //writeln!(file,"  graph [splines=ortho];")?;
-    writeln!(file,"  node [shape=record,style=filled,fontname = \"monospace\"];")?;
-    writeln!(file,"  label=\"GLAS for address space of core {}\";", dn.names[c])?;
-    writeln!(file,"  labelloc=t;")?;
+    writeln!(file, "  node [shape=record,style=filled,fontname = \"monospace\"];")?;
+    writeln!(file, "  label=\"GLAS for address space of core {}\";", dn.names[c])?;
+    writeln!(file, "  labelloc=t;")?;
 
     writeln!(file,"subgraph cluster_glas {{")?;
     writeln!(file,"label = \"GLAS\";")?;
@@ -201,16 +205,16 @@ pub fn visualize_glas_for(
 
     for (r,glas_range,res_range) in glas.placement.iter().rev() {
         writeln!(file,
-            "  \"g_{}\" -> \"{}_{}\"",
-            glas_range.offset,
+            "  \"{}_{}\" -> \"g_{}\"",
             r, res_range.offset,
+            glas_range.offset,
             //(length as f64).log2(),
         )?;
     }
 
     let space = &f.r[&c];
-    for (context_range,_r,resource_range) in space.iter().filter(|(_,r,_)| resources.contains(r)) {
-        let i = glas.placement.iter().position(|(_,_,c)| c == resource_range).unwrap();
+    for (context_range,r,resource_range) in space.iter().filter(|(_,r,_)| resources.contains(r)) {
+        let i = glas.placement.iter().position(|(res,_,c)| r == res && c == resource_range).unwrap();
         let (_r,glas_range,_) = glas.placement[i];
 
         let color = if context_range.offset == glas_range.offset {
@@ -229,9 +233,9 @@ pub fn visualize_glas_for(
         )?;
 
         writeln!(file,
-            "  \"c_{}\" -> \"g_{}\" [color=\"{}\"]",
-            context_range.offset,
+            "  \"g_{}\" -> \"c_{}\" [color=\"{}\"]",
             glas_range.offset,
+            context_range.offset,
             color,
             //(length as f64).log2(),
         )?;
diff --git a/src/range.rs b/src/range.rs
index cd3d8cd50085f39e2d485ef95a898fce46fe62c2..aad87f53a06005965c3329d1280d3973559e1f1f 100644
--- a/src/range.rs
+++ b/src/range.rs
@@ -1,6 +1,8 @@
 use std::cmp::Ordering;
 
-#[derive(Clone, Copy, PartialEq, Debug)]
+use serde::{Serialize, Deserialize};
+
+#[derive(Clone, Copy, PartialEq, Debug, Hash, Serialize, Deserialize)]
 pub struct Range {
     pub offset: u128,
     pub length: u128,
diff --git a/src/range_tree.rs b/src/range_tree.rs
index 841efb991e03b316cdda4c290ea27aecea71bf10..461d2a7d0d1c61b653bdbff4b4da2acff08bb996 100644
--- a/src/range_tree.rs
+++ b/src/range_tree.rs
@@ -1,9 +1,11 @@
 use crate::range::Range;
 
+use serde::{Serialize,Deserialize};
+
 
 /// A range tree is an efficient representation of an address space as a list of ranges. Each range
 /// may be associated with an arbitrary value.
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct RangeTree<V> {
     pub name: String,
     pub top_level: Range,
diff --git a/src/spec.rs b/src/spec.rs
index 77729ee062395d03ca9b7fac32bd295fa7b9ca13..c3716a0088fc252575387c68826d562cb40f1822 100644
--- a/src/spec.rs
+++ b/src/spec.rs
@@ -2,10 +2,9 @@
 //! decoding nets.
 //!
 
-use std::collections::{BTreeSet};
+use std::collections::BTreeSet;
 use std::fmt;
 
-
 use crate::range::Range;
 use serde::Serialize;
 
@@ -171,7 +170,11 @@ impl DecodingNet {
 
         for m in &self.mappings[src.as_usize()] {
             if m.src_range.overlaps(src_range) {
-                return Err(SpecError::AlreadyMapped(src, m.src_range, src_range));
+                return Err(SpecError::AlreadyMapped(
+                    src,
+                    m.src_range,
+                    src_range,
+                ));
             }
         }
 
@@ -230,7 +233,10 @@ impl DecodingNet {
         if range <= address_space.range {
             Ok(())
         } else {
-            Err(SpecError::RangeOutOfBounds(address_space.range, range))
+            Err(SpecError::RangeOutOfBounds(
+                address_space.range,
+                range,
+            ))
         }
     }
 }
@@ -270,6 +276,7 @@ pub enum SpecError {
     AddressSpaceDoesNotExist(String),
 }
 
+
 #[test]
 fn construct_empty_net() {
     let _ = DecodingNet::new();
diff --git a/src/specs.rs b/src/specs.rs
index 1497195208de07363e6daad0567eec6de585609a..b9f82de8f06abeba77e533ff3591f7e24b05c792 100644
--- a/src/specs.rs
+++ b/src/specs.rs
@@ -5,6 +5,7 @@ pub mod common;
 
 pub mod arm;
 pub mod imx8;
+pub mod morello;
 
 pub mod ultrascaleplus;
 
diff --git a/src/specs/arm/v8/mmu.rs b/src/specs/arm/v8/mmu.rs
index 30e6383b34c2bb391bc49a3df4bd3e331cb072b3..c40881cfe4cf70f1a1f64487843eaf026cfefc94 100644
--- a/src/specs/arm/v8/mmu.rs
+++ b/src/specs/arm/v8/mmu.rs
@@ -339,6 +339,20 @@ impl TTBREntry {
             }
         }
     }
+
+    pub fn materialize_into(&self, bytes: &mut [u64]) {
+        match &self {
+            Self::Invalid => (),
+            Self::TableDescriptor {
+                address: base_addr,
+                table: l0_table,
+            } => {
+                for (i, entry) in l0_table.iter().enumerate() {
+                    entry.materialize_into(bytes, *base_addr as u64, i);
+                }
+            }
+        }
+    }
 }
 
 impl fmt::Debug for L0Entry {
@@ -399,6 +413,24 @@ impl L0Entry {
             }
         }
     }
+
+    /// Materialize this entry's configuration, returning a descriptor
+    fn materialize_into(&self, bytes: &mut [u64], base_addr: u64, idx: usize) {
+        bytes[idx] = match &self {
+            Self::Invalid => 0x0,
+            Self::TableDescriptor {
+                address,
+                table,
+            } => {
+                let entry_offset = (address - base_addr as u128) as usize / 8;
+                assert!(entry_offset % 512 == 0);
+                for (i, entry) in table.iter().enumerate() {
+                    entry.materialize_into(bytes, base_addr, entry_offset + i);
+                }
+                (*address as u64 & !0xFFF) | 0b11
+            },
+        };
+    }
 }
 
 impl fmt::Debug for L1Entry {
@@ -477,6 +509,23 @@ impl L1Entry {
 
         Ok(())
     }
+
+    /// Materialize this entry's configuration, returning a descriptor
+    fn materialize_into(&self, bytes: &mut [u64], base_addr: u64, idx: usize) {
+        bytes[idx] = match &self {
+            Self::Invalid => 0x0,
+            Self::TableDescriptor {
+                address,
+                table,
+            } => {
+                for (i, entry) in table.iter().enumerate() {
+                    entry.materialize_into(bytes, base_addr, (address - base_addr as u128) as usize / 8 + i);
+                }
+                (*address as u64 & !0xFFF) | 0b11
+            },
+            Self::BlockDescriptor(address) => (*address as u64 & !0xFFF) | 0x701,
+        };
+    }
 }
 
 impl fmt::Debug for L2Entry {
@@ -556,6 +605,23 @@ impl L2Entry {
         }
         Ok(())
     }
+
+    /// Materialize this entry's configuration, returning a descriptor
+    fn materialize_into(&self, bytes: &mut [u64], base_addr: u64, idx: usize) {
+        bytes[idx] = match &self {
+            Self::Invalid => 0x0,
+            Self::TableDescriptor {
+                address,
+                table,
+            } => {
+                for (i, entry) in table.iter().enumerate() {
+                    entry.materialize_into(bytes, base_addr, (address - base_addr as u128) as usize / 8 + i);
+                }
+                (*address as u64 & !0xFFF) | 0b11
+            },
+            Self::BlockDescriptor(address) => (*address as u64 & !0xFFF) | 0x701,
+        };
+    }
 }
 
 impl fmt::Debug for L3Entry {
@@ -601,6 +667,20 @@ impl L3Entry {
             }
         }
     }
+
+    /// Materialize this entry's configuration, returning a descriptor
+    fn materialize_into(&self, bytes: &mut [u64], _base_addr: u64, idx: usize) {
+        bytes[idx] = match &self {
+            Self::Invalid => 0x0,
+            Self::BlockDescriptor(address) =>
+                // TODO: HACK: FIXME: this is awful.
+                if *address < 0x4000_0000 {
+                    (*address as u64 & !0xFFF) | 0x707
+                } else {
+                    (*address as u64 & !0xFFF) | 0x701
+                },
+        };
+    }
 }
 
 #[test]
diff --git a/src/specs/common.rs b/src/specs/common.rs
index fabbbcdafbe883c3c8e5e34b6a0aeeee7d92b819..0d10fedfc0da276a71799af17c8187c6a8ae39ed 100644
--- a/src/specs/common.rs
+++ b/src/specs/common.rs
@@ -1,9 +1,28 @@
 #![allow(dead_code, unused_variables)]
 
+use std::ops::Add;
+
 pub type Offset = u128;
 pub type Size = u128;
 pub type Address = u128;
 
+#[macro_export]
+macro_rules! size {
+    ( $x:expr, KB) => {
+        $x * SIZE_1KB
+    };
+    ( $x:expr, MB) => {
+        $x * SIZE_1MB
+    };
+    ( $x:expr, GB) => {
+        $x * SIZE_1GB
+    };
+    ( $x:expr, TB) => {
+        $x * SIZE_1TB
+    };
+}
+pub(crate) use size;
+
 /*
  Size variables to make code more readable.
     The i.MX 8X Reference Manual also talks about size, so this is more consistent j. *);
@@ -31,6 +50,7 @@ pub const OFFSET_8KB: Offset = 2_u128.pow(13);
 pub const OFFSET_14GB: Offset = OFFSET_16GB - OFFSET_2GB;
 pub const OFFSET_96KB: Offset = 6 * OFFSET_16KB;
 
+pub const SIZE_2TB: Size = 2_u128.pow(41);
 pub const SIZE_1TB: Size = 2_u128.pow(40);
 pub const SIZE_512GB: Size = 2_u128.pow(39);
 pub const SIZE_256GB: Size = 2_u128.pow(38);
@@ -64,13 +84,26 @@ pub const SIZE_2KB: Size = 2_u128.pow(11);
 pub const SIZE_1KB: Size = 2_u128.pow(10);
 pub const SIZE_256B: Size = 2_u128.pow(8);
 // "Impure" sizes pub constructed from pure ones:
+pub const SIZE_3TB: Size = SIZE_2TB + SIZE_1TB;
+pub const SIZE_510GB: Size = SIZE_512GB - SIZE_2GB;
 pub const SIZE_448GB: Size = SIZE_512GB - SIZE_64GB;
 pub const SIZE_224GB: Size = SIZE_256GB - SIZE_32GB;
 pub const SIZE_28GB: Size = SIZE_32GB - SIZE_4GB;
 pub const SIZE_14GB: Size = SIZE_16GB - SIZE_2GB;
 pub const SIZE_12GB: Size = SIZE_16GB - SIZE_4GB;
+pub const SIZE_192MB: Size = SIZE_128MB + SIZE_64MB;
 pub const SIZE_63MB: Size = SIZE_64MB - SIZE_1MB;
 pub const SIZE_12MB: Size = SIZE_16MB - SIZE_4MB;
+pub const SIZE_10MB: Size = SIZE_12MB - SIZE_2MB;
+pub const SIZE_768KB: Size = SIZE_512KB + SIZE_256KB;
 pub const SIZE_448KB: Size = SIZE_512KB - SIZE_64KB;
 pub const SIZE_96KB: Size = 6 * SIZE_16KB;
 pub const SIZE_80KB: Size = 5 * SIZE_16KB;
+
+pub struct MapInfo {
+    pub name: &'static str,
+    pub description: &'static str,
+    pub start: Option<Address>,
+    pub end: Option<Address>,
+    pub length: Option<Size>,
+}
diff --git a/src/specs/examples.rs b/src/specs/examples.rs
index 7de0bc3e87ad0b6e2209366e98fc9535450b2bee..f2726b2b3653e23d6e6c8d7071f50af22bc452ab 100644
--- a/src/specs/examples.rs
+++ b/src/specs/examples.rs
@@ -1,4 +1,5 @@
 pub mod imx8;
+pub mod morello;
 pub mod mmu;
 pub mod non_transitive_trust;
 pub mod qemu_aarch64;
diff --git a/src/specs/examples/imx8.rs b/src/specs/examples/imx8.rs
index 7a5a20699231b948805c5ef8e935bc677aeade42..684c594ba74c4ce36f21e3059eb5da6a61ef0366 100644
--- a/src/specs/examples/imx8.rs
+++ b/src/specs/examples/imx8.rs
@@ -20,47 +20,47 @@ pub fn instantiate_imx8_example(with_random_xrdc: bool) -> Result<(DecodingNet,
     }
     xrdc.add_to_decoding_net(&mut dn)?;
 
-    let mut l3_table = Box::new(std::array::from_fn(|_| L3Entry::Invalid));
-    let mut l2_table = Box::new(std::array::from_fn(|_| L2Entry::Invalid));
-    let mut l1_table = Box::new(std::array::from_fn(|_| L1Entry::Invalid));
-    let mut l0_table = Box::new(std::array::from_fn(|_| L0Entry::Invalid));
-    l3_table[0] = L3Entry::BlockDescriptor(0xF00F_0000);
-    l2_table[0] = L2Entry::TableDescriptor {
-        address: 0x8000_3000,
-        table: l3_table,
-    };
-    l1_table[0] = L1Entry::TableDescriptor {
-        address: 0x8000_2000,
-        table: l2_table,
-    };
-    l0_table[0] = L0Entry::TableDescriptor {
-        address: 0x8000_1000,
-        table: l1_table,
-    };
-    let ttbr_entry = TTBREntry::TableDescriptor {
-        address: 0x8000_0000,
-        table: l0_table,
-    };
+    //let mut l3_table = Box::new(std::array::from_fn(|_| L3Entry::Invalid));
+    //let mut l2_table = Box::new(std::array::from_fn(|_| L2Entry::Invalid));
+    //let mut l1_table = Box::new(std::array::from_fn(|_| L1Entry::Invalid));
+    //let mut l0_table = Box::new(std::array::from_fn(|_| L0Entry::Invalid));
+    //l3_table[0] = L3Entry::BlockDescriptor(0xF00F_0000);
+    //l2_table[0] = L2Entry::TableDescriptor {
+    //    address: 0x8000_3000,
+    //    table: l3_table,
+    //};
+    //l1_table[0] = L1Entry::TableDescriptor {
+    //    address: 0x8000_2000,
+    //    table: l2_table,
+    //};
+    //l0_table[0] = L0Entry::TableDescriptor {
+    //    address: 0x8000_1000,
+    //    table: l1_table,
+    //};
+    //let ttbr_entry = TTBREntry::TableDescriptor {
+    //    address: 0x8000_0000,
+    //    table: l0_table,
+    //};
 
-    let mmus = imx8.get_a35_mmus();
-    let simple_cfg = Configuration::OneStage(ttbr_entry);
-    let vas0 = mmus[0]
-        .create_virtual_address_space(
-            &mut dn,
-            &simple_cfg,
-            "MMU_DB_A35_0",
-            "Victim address space for analysis",
-        )
-        .expect("mmu configuraiton failed");
+    //let mmus = imx8.get_a35_mmus();
+    //let simple_cfg = Configuration::OneStage(ttbr_entry);
+    //let vas0 = mmus[0]
+    //    .create_virtual_address_space(
+    //        &mut dn,
+    //        &simple_cfg,
+    //        "MMU_DB_A35_0",
+    //        "Victim address space for analysis",
+    //    )
+    //    .expect("mmu configuraiton failed");
 
-    let disabled_cfg = Configuration::Disabled;
-    let vas1 = mmus[1].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_1", "")?;
-    let vas2 = mmus[2].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_2", "")?;
-    let vas3 = mmus[3].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_3", "")?;
-    dn.mark_context(vas0, "");
-    dn.mark_context(vas1, "");
-    dn.mark_context(vas2, "");
-    dn.mark_context(vas3, "");
+    //let disabled_cfg = Configuration::Disabled;
+    //let vas1 = mmus[1].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_1", "")?;
+    //let vas2 = mmus[2].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_2", "")?;
+    //let vas3 = mmus[3].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_3", "")?;
+    //dn.mark_context(vas0, "");
+    //dn.mark_context(vas1, "");
+    //dn.mark_context(vas2, "");
+    //dn.mark_context(vas3, "");
 
     Ok((dn, imx8))
 }
diff --git a/src/specs/examples/mmu.rs b/src/specs/examples/mmu.rs
index e02dd56280b32ca91fa1bf56cd84771efaac7554..e1bacfefb0816a392a3ed8aac4645d1b32622135 100644
--- a/src/specs/examples/mmu.rs
+++ b/src/specs/examples/mmu.rs
@@ -1,17 +1,15 @@
-#![cfg(test)]
-
 use crate::analysis::{self, Assumptions, BehaviorAssumption, Model, StableAssumptions};
 use crate::analysis::{LocalAddressRange, QualifiedAddressRange, ASID};
 use crate::spec::DecodingNet;
 use crate::specs::arm::v8::mmu::{self, *};
 
-struct PageTable {
-    base_table: TTBREntry,
-    page_tracker: PageTracker,
+pub struct PageTable {
+    pub base_table: TTBREntry,
+    pub page_tracker: PageTracker,
 }
 
-struct PageTracker {
-    used_pages: usize,
+pub struct PageTracker {
+    pub used_pages: usize,
     base_addr: u128,
 }
 
@@ -24,7 +22,7 @@ impl PageTracker {
 }
 
 impl PageTable {
-    fn new(base_addr: u128) -> PageTable {
+    pub fn new(base_addr: u128) -> PageTable {
         Self {
             base_table: TTBREntry::TableDescriptor {
                 address: base_addr,
@@ -37,6 +35,92 @@ impl PageTable {
         }
     }
 
+    pub fn add_1g_mapping(&mut self, input_address: u64, output_address: u64) {
+        assert!(input_address % (1 << 30) == 0);
+        assert!(output_address % (1 << 30) == 0);
+
+        let bt = &mut self.base_table;
+        let pt = &mut self.page_tracker;
+
+        let l0_entry = bt.index_table(input_address).unwrap();
+        match l0_entry {
+            L0Entry::Invalid => {
+                *l0_entry = L0Entry::TableDescriptor {
+                    address: pt.next_page(),
+                    table: Box::new(std::array::from_fn(|_| L1Entry::Invalid)),
+                }
+            }
+            L0Entry::TableDescriptor { .. } => (),
+        }
+
+        let l1_entry = l0_entry.index_table(input_address).unwrap();
+        match l1_entry {
+            L1Entry::Invalid => *l1_entry = L1Entry::BlockDescriptor(output_address),
+            L1Entry::TableDescriptor { .. } => unimplemented!(
+                "conflicting mapping at L1 table! requested={:#x} actual=table descriptor",
+                output_address,
+            ),
+            L1Entry::BlockDescriptor(addr) => unimplemented!(
+                "conflicting output addresses! requested={:#x} actual={:#x}",
+                output_address,
+                addr
+            ),
+        };
+    }
+
+
+    pub fn add_2m_mapping(&mut self, input_address: u64, output_address: u64) {
+        assert!(input_address % (1 << 21) == 0);
+        assert!(output_address % (1 << 21) == 0);
+
+        let bt = &mut self.base_table;
+        let pt = &mut self.page_tracker;
+
+        let l0_entry = bt.index_table(input_address).unwrap();
+        match l0_entry {
+            L0Entry::Invalid => {
+                *l0_entry = L0Entry::TableDescriptor {
+                    address: pt.next_page(),
+                    table: Box::new(std::array::from_fn(|_| L1Entry::Invalid)),
+                }
+            }
+            L0Entry::TableDescriptor { .. } => (),
+        }
+
+        let l1_entry = l0_entry.index_table(input_address).unwrap();
+        match l1_entry {
+            L1Entry::Invalid => {
+                *l1_entry = L1Entry::TableDescriptor {
+                    address: pt.next_page(),
+                    table: Box::new(std::array::from_fn(|_| L2Entry::Invalid)),
+                }
+            }
+            L1Entry::TableDescriptor { .. } => (),
+            L1Entry::BlockDescriptor { .. } => unimplemented!(),
+        };
+
+        let l2_entry = l1_entry.index_table(input_address).unwrap();
+        match l2_entry {
+            L2Entry::Invalid => *l2_entry = L2Entry::BlockDescriptor(output_address),
+            L2Entry::TableDescriptor { .. } => unimplemented!(
+                "conflicting mapping at L2 table! requested={:#x} actual=table descriptor",
+                output_address,
+            ),
+            L2Entry::BlockDescriptor(addr) => unimplemented!(
+                "conflicting output addresses! requested={:#x} actual={:#x}",
+                output_address,
+                addr
+            ),
+        };
+    }
+
+    pub fn add_4k_mapping(&mut self, input_address: u64, output_address: u64) {
+        assert!(input_address % (1 << 12) == 0);
+        assert!(output_address % (1 << 12) == 0);
+
+        self.add_single_mapping(input_address, output_address);
+    }
+
     fn add_single_mapping(&mut self, input_address: u64, output_address: u64) {
         let bt = &mut self.base_table;
         let pt = &mut self.page_tracker;
diff --git a/src/specs/examples/morello.rs b/src/specs/examples/morello.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d2511f7903d8b0bd2def4d476414dfedf2bfba4a
--- /dev/null
+++ b/src/specs/examples/morello.rs
@@ -0,0 +1,59 @@
+use rand::Rng;
+
+use crate::spec::{DecodingNet, SpecError};
+use crate::specs::arm::v8::mmu::{Configuration, L0Entry, L1Entry, L2Entry, L3Entry, TTBREntry};
+use crate::specs::morello;
+
+pub fn instantiate_morello_example() -> Result<(DecodingNet, morello::Morello), SpecError> {
+    let mut dn = DecodingNet::new();
+    let mut morello = morello::Morello::new(&mut dn)?;
+
+    //let mut l3_table = Box::new(std::array::from_fn(|_| L3Entry::Invalid));
+    //let mut l2_table = Box::new(std::array::from_fn(|_| L2Entry::Invalid));
+    //let mut l1_table = Box::new(std::array::from_fn(|_| L1Entry::Invalid));
+    //let mut l0_table = Box::new(std::array::from_fn(|_| L0Entry::Invalid));
+    //l3_table[0] = L3Entry::BlockDescriptor(0xF00F_0000);
+    //l2_table[0] = L2Entry::TableDescriptor {
+    //    address: 0x8000_3000,
+    //    table: l3_table,
+    //};
+    //l1_table[0] = L1Entry::TableDescriptor {
+    //    address: 0x8000_2000,
+    //    table: l2_table,
+    //};
+    //l0_table[0] = L0Entry::TableDescriptor {
+    //    address: 0x8000_1000,
+    //    table: l1_table,
+    //};
+    //let ttbr_entry = TTBREntry::TableDescriptor {
+    //    address: 0x8000_0000,
+    //    table: l0_table,
+    //};
+
+    //let mmus = morello.get_a35_mmus();
+    //let simple_cfg = Configuration::OneStage(ttbr_entry);
+    //let vas0 = mmus[0]
+    //    .create_virtual_address_space(
+    //        &mut dn,
+    //        &simple_cfg,
+    //        "MMU_DB_A35_0",
+    //        "Victim address space for analysis",
+    //    )
+    //    .expect("mmu configuraiton failed");
+
+    //let disabled_cfg = Configuration::Disabled;
+    //let vas1 = mmus[1].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_1", "")?;
+    //let vas2 = mmus[2].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_2", "")?;
+    //let vas3 = mmus[3].create_virtual_address_space(&mut dn, &disabled_cfg, "MMU_DB_A35_3", "")?;
+    //dn.mark_context(vas0, "");
+    //dn.mark_context(vas1, "");
+    //dn.mark_context(vas2, "");
+    //dn.mark_context(vas3, "");
+
+    Ok((dn, morello))
+}
+
+#[test]
+fn test() {
+    instantiate_morello_example().expect("could not create morello example");
+}
diff --git a/src/specs/examples/qemu_aarch64.rs b/src/specs/examples/qemu_aarch64.rs
index 6aac5e1a434184feb711ede263fd4f369b042748..7fc36ea0f9c5eab90cfcd0cb97f85bc199244968 100644
--- a/src/specs/examples/qemu_aarch64.rs
+++ b/src/specs/examples/qemu_aarch64.rs
@@ -3,9 +3,9 @@ use crate::spec::SpecError;
 
 pub fn create_dn() -> Result<spec::DecodingNet, SpecError> {
     let mut dn = spec::DecodingNet::new();
-    let c0 = dn.add_address_space("cpu core 0", "", 0, 1 << 36)?;
-    let c1 = dn.add_address_space("cpu core 1", "", 0, 1 << 36)?;
-    let c2 = dn.add_address_space("cpu core 2", "", 0, 1 << 36)?;
+    let c0 = dn.add_address_space("cpu_core_0", "", 0, 1 << 36)?;
+    let c1 = dn.add_address_space("cpu_core_1", "", 0, 1 << 36)?;
+    let c2 = dn.add_address_space("cpu_core_2", "", 0, 1 << 36)?;
     let r = dn.add_address_space("ram", "", 0, 3 * (1 << 30))?;
     let uart = dn.add_address_space("uart0", "", 0, 1 << 12)?;
 
diff --git a/src/specs/imx8.rs b/src/specs/imx8.rs
index 971e88a9c9ba9481c4198a7b4580dd0084543515..e85d85cf712241cffdf5c9bd8bad468958d2f6b2 100644
--- a/src/specs/imx8.rs
+++ b/src/specs/imx8.rs
@@ -3,7 +3,7 @@
 use crate::spec::{ASID};
 use crate::spec::{DecodingNet, SpecError};
 
-use crate::specs::arm::v8::mmu::MMU;
+//use crate::specs::arm::v8::mmu::MMU;
 use crate::specs::common::*;
 
 use self::xrdc2::XRDC2;
@@ -37,10 +37,10 @@ pub struct Imx8 {
     cortex_a35_2: ASID,
     cortex_a35_3: ASID,
 
-    cortex_a35_0_mmu: MMU,
-    cortex_a35_1_mmu: MMU,
-    cortex_a35_2_mmu: MMU,
-    cortex_a35_3_mmu: MMU,
+    //cortex_a35_0_mmu: MMU,
+    //cortex_a35_1_mmu: MMU,
+    //cortex_a35_2_mmu: MMU,
+    //cortex_a35_3_mmu: MMU,
 
     cortex_m4: ASID,
     system_control_unit: ASID,
@@ -581,10 +581,10 @@ impl Imx8 {
             cortex_a35_2: a35_2_interconnect_view,
             cortex_a35_3: a35_3_interconnect_view,
 
-            cortex_a35_0_mmu: MMU::new(a35_0_interconnect_view, a35_0_interconnect_view),
-            cortex_a35_1_mmu: MMU::new(a35_1_interconnect_view, a35_1_interconnect_view),
-            cortex_a35_2_mmu: MMU::new(a35_2_interconnect_view, a35_2_interconnect_view),
-            cortex_a35_3_mmu: MMU::new(a35_3_interconnect_view, a35_3_interconnect_view),
+            //cortex_a35_0_mmu: MMU::new(a35_0_interconnect_view, a35_0_interconnect_view),
+            //cortex_a35_1_mmu: MMU::new(a35_1_interconnect_view, a35_1_interconnect_view),
+            //cortex_a35_2_mmu: MMU::new(a35_2_interconnect_view, a35_2_interconnect_view),
+            //cortex_a35_3_mmu: MMU::new(a35_3_interconnect_view, a35_3_interconnect_view),
 
             cortex_m4: m4_interconnect_view,
             system_control_unit: sc_interconnect_view,
@@ -659,14 +659,14 @@ impl Imx8 {
         self.enet2
     }
 
-    pub fn get_a35_mmus(&self) -> [&MMU; 4] {
-        [
-            &self.cortex_a35_0_mmu,
-            &self.cortex_a35_1_mmu,
-            &self.cortex_a35_2_mmu,
-            &self.cortex_a35_3_mmu,
-        ]
-    }
+    //pub fn get_a35_mmus(&self) -> [&MMU; 4] {
+    //    [
+    //        &self.cortex_a35_0_mmu,
+    //        &self.cortex_a35_1_mmu,
+    //        &self.cortex_a35_2_mmu,
+    //        &self.cortex_a35_3_mmu,
+    //    ]
+    //}
 
     /// Return the address space corresponding to the Cortex-M4 user core.
     pub fn get_m4(&self) -> ASID {
diff --git a/src/specs/imx8/audio_dma/hifi4_dsp.rs b/src/specs/imx8/audio_dma/hifi4_dsp.rs
index adb716d6de264a0d738ab1f040c8bff20b4a59da..18f5e092ec40900d911d96112a8f5c9ed438e2b8 100644
--- a/src/specs/imx8/audio_dma/hifi4_dsp.rs
+++ b/src/specs/imx8/audio_dma/hifi4_dsp.rs
@@ -41,19 +41,19 @@ impl HiFi4Dsp {
         )?;
         let dsp_iram0_asid = dn.add_address_space(
             "DSP_INSTRUCTION_RAM_0",
-            "DSP instruction RAM, as per IMX8DQXPRM @ p. 8078, Table 16-140.",
+            "DSP instruction RAM, as per IMX8DQXPRM @ p. 8078, Table 16-140.\nWe have no idea where this is mapped.",
             0,
             SIZE_2KB,
         )?;
         let dsp_dram0_asid = dn.add_address_space(
             "DSP_DATA_RAM_0",
-            "DSP data RAM 0, as per IMX8DQXPRM @ p. 8078, Table 16-140.",
+            "DSP data RAM 0, as per IMX8DQXPRM @ p. 8078, Table 16-140.\nWe have no idea where this is mapped.",
             0,
             SIZE_32KB,
         )?;
         let dsp_dram1_asid = dn.add_address_space(
             "DSP_DATA_RAM_1",
-            "DSP data RAM 1, as per IMX8DQXPRM @ p. 8078, Table 16-140.",
+            "DSP data RAM 1, as per IMX8DQXPRM @ p. 8078, Table 16-140.\nWe have no idea where this is mapped.",
             0,
             SIZE_32KB,
         )?;
diff --git a/src/specs/imx8/cm4.rs b/src/specs/imx8/cm4.rs
index 0396009cf51e92793c8a1d8ee0e077f78b07b787..91f0564961d9010f0c45689ab4bb70707dd85536 100644
--- a/src/specs/imx8/cm4.rs
+++ b/src/specs/imx8/cm4.rs
@@ -144,6 +144,7 @@ impl CortexM4 {
             0,
             7 * SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_lpcg, "")?;
 
         // Todo: Move MU to MU model
         let cm4_mu1_a = dn.add_address_space(
@@ -152,36 +153,42 @@ impl CortexM4 {
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_mu1_a, "")?;
         let cm4_mu0_a3 = dn.add_address_space(
             &format!("{} MU0_A3", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_mu0_a3, "")?;
         let cm4_mu0_a2 = dn.add_address_space(
             &format!("{} MU0_A2", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_mu0_a2, "")?;
         let cm4_mu0_a1 = dn.add_address_space(
             &format!("{} MU0_A1", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_mu0_a1, "")?;
         let cm4_mu0_a0 = dn.add_address_space(
             &format!("{} MU0_A0", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_mu0_a0, "")?;
         let cm4_mu0_b = dn.add_address_space(
             &format!("{} MU0_B", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_mu0_b, "")?;
 
         let cm4_wdog = dn.add_address_space(
             &format!("{} WDOG", name),
@@ -189,18 +196,21 @@ impl CortexM4 {
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_wdog, "")?;
         let cm4_asmc = dn.add_address_space(
             &format!("{} ASMC", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_asmc, "")?;
         let cm4_intmux = dn.add_address_space(
             &format!("{} INTMUX", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_intmux, "")?;
 
         let cm4_lpi2c = dn.add_address_space(
             &format!("{} LPI2C", name),
@@ -208,24 +218,28 @@ impl CortexM4 {
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_lpi2c, "")?;
         let cm4_lpuart = dn.add_address_space(
             &format!("{} LPUART", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_lpuart, "")?;
         let cm4_lpit = dn.add_address_space(
             &format!("{} LPIT", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_lpit, "")?;
         let cm4_tpm = dn.add_address_space(
             &format!("{} TPM", name),
             "as per IMX8DQXPRM @ p. 49",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_tpm, "")?;
 
         let cm4_sema42 = dn.add_address_space(
             &format!("{} SEMA42", name),
@@ -233,6 +247,7 @@ impl CortexM4 {
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_sema42, "")?;
 
         let cm4_debug = dn.add_address_space(
             &format!("{} Debug Block", name),
@@ -240,6 +255,7 @@ impl CortexM4 {
             0,
             SIZE_12MB,
         )?;
+        dn.mark_resource(cm4_debug, "")?;
 
         let cm4_syscnt_cmp = dn.add_address_space(
             &format!("{} SYSCNT CMP", name),
@@ -247,18 +263,21 @@ impl CortexM4 {
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_syscnt_cmp, "")?;
         let cm4_syscnt_rd = dn.add_address_space(
             &format!("{} SYSCNT RD", name),
             "as per IMX8DQXPRM @ p. 45",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_syscnt_rd, "")?;
         let cm4_syscnt_ctrl = dn.add_address_space(
             &format!("{} SYSCNT CTRL", name),
             "as per IMX8DQXPRM @ p. 45",
             0,
             SIZE_64KB,
         )?;
+        dn.mark_resource(cm4_syscnt_ctrl, "")?;
 
         /* CM4 Local View  */
         xrdc.add_mapping(
diff --git a/src/specs/morello.rs b/src/specs/morello.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9b6d48b0d9fcb2413631228b741b97086123e1c2
--- /dev/null
+++ b/src/specs/morello.rs
@@ -0,0 +1,544 @@
+use std::collections::HashMap;
+
+use crate::spec::ASID;
+use crate::spec::{DecodingNet, SpecError};
+
+use crate::specs::common::*;
+
+use super::imx8::xrdc2::ProspectivePeripheral;
+
+mod sdp_ap_map;
+mod sdp_ap_sub_iofpga_map;
+mod sdp_ap_sub_peripherals_map;
+mod sdp_mcp_map;
+mod sdp_mcp_sub_peripherals_map;
+mod sdp_mcp_sub_peripherals_scp2_mhu_map;
+mod sdp_scp_map;
+mod sdp_scp_sub_peripherals_map;
+
+pub struct Morello {}
+
+fn add_sub_as_addr(
+    dn: &mut DecodingNet,
+    parent: ASID,
+    parent_offset: Address,
+    name: &str,
+    description: &str,
+    start: Address,
+    end: Address,
+) -> Result<ASID, SpecError> {
+    add_sub_as_len(
+        dn,
+        parent,
+        parent_offset,
+        name,
+        description,
+        start,
+        end - start,
+    )
+}
+fn add_sub_as_len(
+    dn: &mut DecodingNet,
+    parent: ASID,
+    parent_offset: Address,
+    name: &str,
+    description: &str,
+    start: Address,
+    length: Size,
+) -> Result<ASID, SpecError> {
+    let parent_name = dn.address_spaces[parent.as_usize()].name.clone();
+    let child = dn.add_address_space(
+        &(parent_name.clone() + "/" + name),
+        description,
+        0x0,
+        length,
+    )?;
+    let end = start + length;
+    println!(
+        "Adding {:?}, start: {:#x?}, end: {:#x?}, end-start: {:#x?}, length: {:#x?}",
+        name,
+        start,
+        end,
+        end - start,
+        length
+    );
+    dn.add_mapping(parent, child, start - parent_offset, 0x0, length, "")?;
+
+    return Ok(child);
+}
+
+fn add_sub_as_both(
+    dn: &mut DecodingNet,
+    parent: ASID,
+    parent_offset: Address,
+    name: &str,
+    description: &str,
+    start: Address,
+    end: Address,
+    length: Size,
+) -> Result<ASID, SpecError> {
+    assert!(end - start == length);
+    add_sub_as_len(dn, parent, parent_offset, name, description, start, length)
+}
+
+fn add_from_mapinfo(
+    dn: &mut DecodingNet,
+    parent: ASID,
+    parent_offset: Address,
+    mapinfos: &[MapInfo],
+) -> Result<HashMap<String, (ASID, Address)>, SpecError> {
+    let mut result = HashMap::new();
+
+    for mapinfo in mapinfos {
+        let asid;
+
+        match (mapinfo.start, mapinfo.end, mapinfo.length) {
+            (Some(start), Some(end), Some(length)) => {
+                asid = add_sub_as_both(
+                    dn,
+                    parent,
+                    parent_offset,
+                    mapinfo.name,
+                    &mapinfo.description,
+                    start,
+                    end,
+                    length,
+                )?;
+            }
+
+            (Some(start), Some(end), None) => {
+                asid = add_sub_as_addr(
+                    dn,
+                    parent,
+                    parent_offset,
+                    mapinfo.name,
+                    &mapinfo.description,
+                    start,
+                    end,
+                )?;
+            }
+
+            (Some(start), None, Some(length)) => {
+                asid = add_sub_as_len(
+                    dn,
+                    parent,
+                    parent_offset,
+                    mapinfo.name,
+                    &mapinfo.description,
+                    start,
+                    length,
+                )?;
+            }
+
+            _ => {
+                unimplemented!("");
+            }
+        }
+
+        if result.contains_key(&mapinfo.name.to_string()) {
+            return Err(SpecError::AddressSpaceAlreadyExists(format!("Child with key {:?} already exists for {:?}", mapinfo.name, dn.address_spaces[parent.as_usize()].name)));
+
+        }
+        result.insert(mapinfo.name.to_string(), (asid, mapinfo.start.unwrap()));
+    }
+
+    return Ok(result);
+}
+
+fn child_mark_resource(
+    dn: &mut DecodingNet,
+    children: &HashMap<String, (ASID, Address)>,
+    name: &str,
+    description: &str,
+) -> Result<(), SpecError> {
+    dn.mark_resource(children.get(name).unwrap().0, description)
+}
+
+fn child_mark_all_resource_except(
+    dn: &mut DecodingNet,
+    children: &HashMap<String, (ASID, Address)>,
+    names: &[&str],
+    description: &str,
+) -> Result<(), SpecError> {
+    for (name, (asid, _)) in children.iter() {
+        if !names.contains(&name.as_str()) {
+            dn.mark_resource(*asid, description)?;
+        }
+    }
+    Ok(())
+}
+
+#[must_use]
+fn coalesce_equivalent_as_into_resource(
+    dn: &mut DecodingNet,
+    target_name: &str,
+    equivalent_names: &[(&str, Address)],
+) -> Result<ASID, SpecError> {
+    println!("Adding coalesced resource {}", target_name);
+
+    let mut equivalent_asids = Vec::new();
+    for (idx, (name, offset)) in equivalent_names.iter().enumerate() {
+        equivalent_asids.push((dn.get_address_space_by_name(name).unwrap(), offset));
+    }
+    if equivalent_asids.is_empty() {
+        return Err(SpecError::AddressSpaceDoesNotExist("".to_string()));
+    }
+
+    let normative_range = dn.address_spaces[equivalent_asids.get(0).unwrap().0.as_usize()].range;
+    let mut target_length = 0;
+    for (asid, &offset) in &equivalent_asids {
+        target_length = offset + dn.address_spaces[asid.as_usize()].range.length;
+    }
+
+    let target_asid = dn.add_address_space(
+        &("resource_".to_owned() + target_name),
+        "",
+        0x0,
+        target_length,
+    )?;
+    dn.mark_resource(target_asid, "")?;
+    for (asid, &offset) in &equivalent_asids {
+        dn.add_mapping(
+            *asid,
+            target_asid,
+            0x0,
+            offset,
+            dn.address_spaces[asid.as_usize()].range.length,
+            "",
+        )?;
+    }
+
+    return Ok(target_asid);
+}
+
+impl Morello {
+    pub fn new(dn: &mut DecodingNet) -> Result<Morello, SpecError> {
+        let document_name = "ARM TRM 102278_0001_04_en, Issue 04";
+
+        let mcp_debug_address_translation: bool = false;
+        let scp_debug_address_translation: bool = false;
+
+        let ap_memory_map = dn.add_address_space("ap_memory_map", "", 0x0, 0x4000_0000_0000)?;
+        let ap_children = add_from_mapinfo(dn, ap_memory_map, 0x0, self::sdp_ap_map::MAPINFOS)?;
+
+        let ap_core0 = dn.add_address_space("ap_core0", "", 0x0, 0x4000_0000_0000)?;
+        let ap_core1 = dn.add_address_space("ap_core1", "", 0x0, 0x4000_0000_0000)?;
+        let ap_core2 = dn.add_address_space("ap_core2", "", 0x0, 0x4000_0000_0000)?;
+        let ap_core3 = dn.add_address_space("ap_core3", "", 0x0, 0x4000_0000_0000)?;
+        //dn.mark_context(ap_core0, "");
+        //dn.mark_context(ap_core1, "");
+        //dn.mark_context(ap_core2, "");
+        //dn.mark_context(ap_core3, "");
+        dn.mark_context(ap_memory_map, "HACK: mark memory map as context since it has the same view.\nIt takes too long (>3h) when the ap_cores are contexts too");
+
+        dn.add_mapping(ap_core0, ap_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+        dn.add_mapping(ap_core1, ap_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+        dn.add_mapping(ap_core2, ap_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+        dn.add_mapping(ap_core3, ap_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+
+        let gbe_controller = dn.add_address_space("GbE_controller", "RealTek RTL8111GS", 0x0, 0x4000_0000_0000)?;
+        let sata_controller = dn.add_address_space("sata_controller", "Marvell 88SE9170 SATA 3.0", 0x0, 0x4000_0000_0000)?;
+        let usb_controller = dn.add_address_space("usb_3.0_host_controller", "Texas Instruments TUSB7340IRKMT", 0x0, 0x4000_0000_0000)?;
+        let pcie_memory_map = dn.add_address_space("pcie_memory_map", "", 0x0, 0x4000_0000_0000)?;
+
+        dn.add_mapping(gbe_controller, pcie_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+        dn.add_mapping(usb_controller, pcie_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+        dn.add_mapping(sata_controller, pcie_memory_map, 0x0, 0x0, 0x4000_0000_0000, "")?;
+
+        dn.mark_context(pcie_memory_map, "HACK: mark pcie as context instead of subsystems to decrease derivation time.");
+
+        let smmu = dn.add_address_space("smmu", "", 0x0, 0x4000_0000_0000)?;
+        dn.add_mapping(pcie_memory_map, smmu, 0, 0, 0x4000_0000_0000, "")?;
+
+        dn.add_mapping(smmu, ap_memory_map, 0, 0, 0x4000_0000_0000, "")?;
+
+        child_mark_all_resource_except(
+            dn,
+            &ap_children,
+            &["iofpga_map_window", "subsystem_peripherals" ,"dram0","dram1", "dram2"],
+            ""
+        )?;
+
+        let (ap_subsystem_iofpga_asid, ap_subsystem_iofpga_offset) =
+            ap_children.get("iofpga_map_window").unwrap();
+        let ap_subsystem_iofpga_children = add_from_mapinfo(
+            dn,
+            *ap_subsystem_iofpga_asid,
+            *ap_subsystem_iofpga_offset,
+            self::sdp_ap_sub_iofpga_map::MAPINFOS,
+        )?;
+
+        //child_mark_resource(dn, &ap_subsystem_iofpga_children, "ddr3", "")?;
+        //child_mark_resource(
+        //    dn,
+        //    &ap_subsystem_iofpga_children,
+        //    "scp_backup_boot_memory",
+        //    "",
+        //)?;
+        //child_mark_resource(
+        //    dn,
+        //    &ap_subsystem_iofpga_children,
+        //    "mcp_backup_boot_memory",
+        //    "",
+        //)?;
+        //child_mark_resource(dn, &ap_subsystem_iofpga_children, "system_registers", "")?;
+        //child_mark_resource(dn, &ap_subsystem_iofpga_children, "sram", "")?;
+
+        child_mark_all_resource_except(dn, &ap_subsystem_iofpga_children, &[], "")?;
+
+        let (ap_subsystem_peripherals_asid, ap_subsystem_peripherals_offset) =
+            ap_children.get("subsystem_peripherals").unwrap();
+        let ap_subsystem_peripherals_children = add_from_mapinfo(
+            dn,
+            *ap_subsystem_peripherals_asid,
+            *ap_subsystem_peripherals_offset,
+            self::sdp_ap_sub_peripherals_map::MAPINFOS,
+        )?;
+
+        //child_mark_resource(dn, &ap_subsystem_peripherals_children, "memory_element", "")?;
+        child_mark_all_resource_except(
+            dn,
+            &ap_subsystem_peripherals_children,
+            &[
+                "ap2mcp_mhu_non_secure_ram",
+                "ap2mcp_mhu_secure_ram",
+                "scp_mhu_non_secure_ram",
+                "scp_mhu_secure_ram",
+            ],
+            "",
+        )?;
+
+        let mcp_memory_map = dn.add_address_space("mcp_memory_map", "", 0x0, 0x01_0000_0000)?;
+        dn.mark_context(mcp_memory_map, "");
+        let mcp_children = add_from_mapinfo(dn, mcp_memory_map, 0x0, self::sdp_mcp_map::MAPINFOS)?;
+
+        child_mark_all_resource_except(
+            dn,
+            &mcp_children,
+            &[
+                "mcp_peripherals",
+                "scp2_mhu",
+                "system_access_port_translated_to_0x0_4000_0000_to_0x0_7fff_ffff_of_ap_memory_map",
+                "system_access_port_translated_to_0x0_0000_0000_to_0x0_3fff_ffff_of_ap_memory_map_with_debug_address_translation_not_enabled_translated_to_0x4_0000_0000_to_0x4_3fff_ffff_of_ap_memory_map_with_debug_address_translation_enabled",
+            ],
+            "",
+        )?;
+
+        //child_mark_resource(dn, &mcp_children, "code_boot_rom", "")?;
+        //child_mark_resource(dn, &mcp_children, "code_tcram", "")?;
+        //child_mark_resource(dn, &mcp_children, "sram_dtcram", "")?;
+
+        let (mcp_periperals_asid, mcp_peripherals_offset) =
+            mcp_children.get("mcp_peripherals").unwrap();
+        let mcp_peripherals_children = add_from_mapinfo(
+            dn,
+            *mcp_periperals_asid,
+            *mcp_peripherals_offset,
+            self::sdp_mcp_sub_peripherals_map::MAPINFOS,
+        )?;
+        child_mark_all_resource_except(
+            dn,
+            &mcp_peripherals_children,
+            &["ap2_mcp_mhu_non_secure_ram", "ap2_mcp_mhu_secure_ram"],
+            "",
+        )?;
+
+        let (mcp_periperals_asid, mcp_peripherals_scp2_mhu_offset) =
+            mcp_children.get("scp2_mhu").unwrap();
+        let mcp_peripherals_scp2_mhu_children = add_from_mapinfo(
+            dn,
+            *mcp_periperals_asid,
+            *mcp_peripherals_scp2_mhu_offset,
+            self::sdp_mcp_sub_peripherals_scp2_mhu_map::MAPINFOS,
+        )?;
+        child_mark_all_resource_except(dn, &mcp_peripherals_scp2_mhu_children, &[], "")?;
+
+        let (mcp_sap1_asid, mcp_sap1_offset) = mcp_children
+            .get("system_access_port_translated_to_0x0_4000_0000_to_0x0_7fff_ffff_of_ap_memory_map")
+            .unwrap();
+        dn.add_mapping(*mcp_sap1_asid, ap_memory_map, 0x0, 0x0, SIZE_1GB, "")?;
+
+        let (mcp_sap2_asid, mcp_sap2_offset) = mcp_children.get("system_access_port_translated_to_0x0_0000_0000_to_0x0_3fff_ffff_of_ap_memory_map_with_debug_address_translation_not_enabled_translated_to_0x4_0000_0000_to_0x4_3fff_ffff_of_ap_memory_map_with_debug_address_translation_enabled").unwrap();
+        let mcp_sap2_offset: Address;
+        if mcp_debug_address_translation {
+            mcp_sap2_offset = 0x4_0000_0000;
+        } else {
+            mcp_sap2_offset = 0x0;
+        }
+        dn.add_mapping(
+            *mcp_sap2_asid,
+            ap_memory_map,
+            0x0,
+            mcp_sap2_offset,
+            SIZE_1GB,
+            "",
+        )?;
+
+        let scp_memory_map = dn.add_address_space("scp_memory_map", "", 0x0, 0x01_0000_0000)?;
+        dn.mark_context(scp_memory_map, "");
+        let scp_children = add_from_mapinfo(dn, scp_memory_map, 0x0, self::sdp_scp_map::MAPINFOS)?;
+
+        //child_mark_resource(dn, &scp_children, "code_boot_rom", "")?;
+        //child_mark_resource(dn, &scp_children, "code_tcram", "")?;
+        //child_mark_resource(dn, &scp_children, "sram_dtcram", "")?;
+
+        child_mark_all_resource_except(
+            dn,
+            &scp_children,
+            &[
+                "scp_peripherals",
+                "system_access_port_translated_to_0x0_4000_0000_to_0x0_7fff_ffff_of_ap_memory_map",
+                "system_access_port_translated_to_0x0_0000_0000_to_0x0_3fff_ffff_of_ap_memory_map_with_debug_address_translation_not_enabled_translated_to_0x4_0000_0000_to_0x4_3fff_ffff_of_ap_memory_map_with_debug_address_translation_enabled",
+            ],
+            "",
+        )?;
+
+        let (scp_periperals_asid, scp_peripherals_offset) =
+            scp_children.get("scp_peripherals").unwrap();
+        let scp_peripherals_children = add_from_mapinfo(
+            dn,
+            *scp_periperals_asid,
+            *scp_peripherals_offset,
+            self::sdp_scp_sub_peripherals_map::MAPINFOS,
+        )?;
+        child_mark_all_resource_except(
+            dn,
+            &scp_peripherals_children,
+            &[
+                "scp2_mcp_mhu_non_secura_ram",
+                "scp2_mcp_mhu_secure_ram",
+                "ap2scp_mhu_non_secure_ram",
+                "ap2scp_mhu_secure_ram",
+            ],
+            "",
+        )?;
+
+        let (scp_sap1_asid, scp_sap1_offset) = scp_children
+            .get("system_access_port_translated_to_0x0_4000_0000_to_0x0_7fff_ffff_of_ap_memory_map")
+            .unwrap();
+        dn.add_mapping(*scp_sap1_asid, ap_memory_map, 0x0, 0x0, SIZE_1GB, "")?;
+
+        let (scp_sap2_asid, scp_sap2_offset) = scp_children.get("system_access_port_translated_to_0x0_0000_0000_to_0x0_3fff_ffff_of_ap_memory_map_with_debug_address_translation_not_enabled_translated_to_0x4_0000_0000_to_0x4_3fff_ffff_of_ap_memory_map_with_debug_address_translation_enabled").unwrap();
+        let scp_sap2_offset: Address;
+        if scp_debug_address_translation {
+            scp_sap2_offset = 0x4_0000_0000;
+        } else {
+            scp_sap2_offset = 0x0;
+        }
+        dn.add_mapping(
+            *scp_sap2_asid,
+            ap_memory_map,
+            0x0,
+            scp_sap2_offset,
+            SIZE_1GB,
+            "",
+        )?;
+
+        let gpu_memory_map = dn.add_address_space("gpu_memory_map", "", 0x0, 0x4000_0000_0000)?;
+        dn.mark_context(gpu_memory_map, "");
+        dn.add_mapping(
+            gpu_memory_map,
+            ap_memory_map,
+            0x0,
+            0x0,
+            0x3FFF_FFFF_FFFF,
+            "",
+        )?;
+        let dpu_memory_map = dn.add_address_space("dpu_memory_map", "", 0x0, 0x4000_0000_0000)?;
+        dn.mark_context(dpu_memory_map, "");
+        dn.add_mapping(
+            dpu_memory_map,
+            ap_memory_map,
+            0x0,
+            0x0,
+            0x3FFF_FFFF_FFFF,
+            "",
+        )?;
+
+        let resource_ap2scp_mhu_non_secure_ram = coalesce_equivalent_as_into_resource(
+            dn,
+            "ap2scp_mhu_non_secure_ram",
+            &[
+                (
+                    "ap_memory_map/subsystem_peripherals/scp_mhu_non_secure_ram",
+                    0x0,
+                ),
+                (
+                    "scp_memory_map/scp_peripherals/ap2scp_mhu_non_secure_ram",
+                    0x0,
+                ),
+            ],
+        )?;
+        let resource_ap2scp_mhu_secure_ram = coalesce_equivalent_as_into_resource(
+            dn,
+            "ap2scp_mhu_secure_ram",
+            &[
+                (
+                    "ap_memory_map/subsystem_peripherals/scp_mhu_secure_ram",
+                    0x0,
+                ),
+                ("scp_memory_map/scp_peripherals/ap2scp_mhu_secure_ram", 0x0),
+            ],
+        )?;
+
+        let resource_ap2mcp_mhu_non_secure_ram = coalesce_equivalent_as_into_resource(
+            dn,
+            "ap2mcp_mhu_non_secure_ram",
+            &[
+                (
+                    "ap_memory_map/subsystem_peripherals/ap2mcp_mhu_non_secure_ram",
+                    0x0,
+                ),
+                (
+                    "mcp_memory_map/mcp_peripherals/ap2_mcp_mhu_non_secure_ram",
+                    0x0,
+                ),
+            ],
+        )?;
+        let resource_ap2mcp_mhu_secure_ram = coalesce_equivalent_as_into_resource(
+            dn,
+            "ap2mcp_mhu_secure_ram",
+            &[
+                (
+                    "ap_memory_map/subsystem_peripherals/ap2mcp_mhu_secure_ram",
+                    0x0,
+                ),
+                ("mcp_memory_map/mcp_peripherals/ap2_mcp_mhu_secure_ram", 0x0),
+            ],
+        )?;
+
+        let resource_scp2mcp_mhu_non_secure_ram = coalesce_equivalent_as_into_resource(
+            dn,
+            "scp2mcp_mhu_non_secure_ram",
+            &[
+                (
+                    "scp_memory_map/scp_peripherals/scp2mcp_mhu_non_secure_ram",
+                    0x0,
+                ),
+                ("mcp_memory_map/scp2_mhu/scp2_mcp_mhu_non_secure_ram", 0x0),
+            ],
+        )?;
+        let resource_scp2mcp_mhu_secure_ram = coalesce_equivalent_as_into_resource(
+            dn,
+            "scp2mcp_mhu_secure_ram",
+            &[
+                ("scp_memory_map/scp_peripherals/scp2mcp_mhu_secure_ram", 0x0),
+                ("mcp_memory_map/scp2_mhu/scp2_mcp_mhu_secure_ram", 0x0),
+            ],
+        )?;
+
+        let resource_dram = coalesce_equivalent_as_into_resource(
+            dn,
+            "dram",
+            &[
+                ("ap_memory_map/dram0", 0x0),
+                ("ap_memory_map/dram1", SIZE_2GB),
+                ("ap_memory_map/dram2", SIZE_512GB),
+            ],
+        )?;
+
+        let mut morello = Morello {};
+
+        return Ok(morello);
+    }
+}
diff --git a/src/specs/morello/raw/README.md b/src/specs/morello/raw/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..269014bc5b6db6ce769deef7edd871edccb2f3d5
--- /dev/null
+++ b/src/specs/morello/raw/README.md
@@ -0,0 +1,6 @@
+# Raw CSV files
+
+These files were extracted from the Morello specification documents. They
+contain a Python script that transforms the CSV tables into Rust source code.
+
+Modifications to the CSV files will not be picked up by the spec automatically.
diff --git a/src/specs/morello/raw/input/morello/sdp_ap_map.csv b/src/specs/morello/raw/input/morello/sdp_ap_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..b5526d4f5b02cd36452a59fb7af346f7a314b1c7
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_ap_map.csv
@@ -0,0 +1,21 @@
+0x0_0000_0000 0x0_07FF_FFFF 128MB Boot region
+0x0_0800_0000 0x0_1fff_ffff Invalid|Custom|NotCalculated IOFPGA Map Window
+0x0_2000_0000 0x0_207F_FFFF 8MB PCIe rc (APB)
+0x0_2080_0000 0x0_2084_FFFF 320KB PCIe PHY (APB)
+0x0_2085_0000 0x0_2085_FFFF 64KB PCIe msg (APB)
+0x0_2100_0000 0x0_217F_FFFF 8MB CCIX rc (APB)
+0x0_2180_0000 0x0_2184_FFFF 320KB CCIX PHY (APB)
+0x0_2185_0000 0x0_2185_FFFF 64KB CCIX msg (APB)
+0x0_2200_0000 0x0_220F_FFFF 1MB NIC-400 SoC GPV
+0x0_2210_0000 0x0_2210_FFFF 64KB GPIO
+0x0_2A00_0000 0x0_4FFF_FFFF 608MB Subsystem peripherals
+0x0_5000_0000 0x0_5FFF_FFFF 256MB CMN-Skeena GPV
+0x0_6000_0000 0x0_6FFF_FFFF 256MB PCIe subordinate AXI 1
+0x0_7000_0000 0x0_7FFF_FFFF 256MB CCIX subordinate AXI 1
+0x0_8000_0000 0x0_FFFF_FFFF 2GB DRAM0
+0x4_0000_0000 0x4_FFFF_FFFF 4GB CoreSight subsystem
+0x5_0000_0000 0x8_FFFF_FFFF 16GB IOFPGA TLX manager IF
+0x9_0000_0000 0x28_FFFF_FFFF 128GB PCIe subordinate AXI 2
+0x30_0000_0000 0x4F_FFFF_FFFF 128GB CCIX subordinate AXI 2
+0x80_8000_0000 0xFF_FFFF_FFFF 510GB DRAM1
+0x100_0000_0000 0x3FF_FFFF_FFFF 3TB DRAM2
diff --git a/src/specs/morello/raw/input/morello/sdp_ap_sub_iofpga_map.csv b/src/specs/morello/raw/input/morello/sdp_ap_sub_iofpga_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..25124e13fb1ef367fb15b959efa140003f8ca302
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_ap_sub_iofpga_map.csv
@@ -0,0 +1,31 @@
+0x0800_0000 0x13FF_FFFF 192MB DDR3
+0x1400_0000 0x15FF_FFFF 32MB SCP backup boot memory
+0x1600_0000 0x17FF_FFFF 32MB MCP backup boot memory
+0x1800_0000 0x19FF_FFFF 32MB QSPI (SCP/MCP)
+0x1A00_0000 0x1BFF_FFFF 32MB QSPI (AP)
+0x1C00_0000 0x1C00_FFFF 64KB I2S
+0x1C01_0000 0x1C01_FFFF 64KB System registers
+0x1C02_0000 0x1C02_FFFF 64KB System control
+0x1C03_0000 0x1C03_FFFF 64KB PCIe SW I2C
+0x1C04_0000 0x1C04_FFFF 64KB SoC DDR SPD
+0x1C05_0000 0x1C05_FFFF 64KB QSPI configuration (AP)
+0x1C06_0000 0x1C06_FFFF 64KB SDIO microSD configuration
+0x1C07_0000 0x1C07_FFFF 64KB SDIO eMMC configuration
+0x1C08_0000 0x1C08_FFFF 64KB Reserved 1
+0x1C09_0000 0x1C09_FFFF 64KB FPGAUART0
+0x1C0A_0000 0x1C0A_FFFF 64KB FPGAUART1
+0x1C0B_0000 0x1C0B_FFFF 64KB Watchdog
+0x1C0C_0000 0x1C0C_FFFF 64KB QSPI SPI configuration (AP)
+0x1C0D_0000 0x1C0D_FFFF 64KB Dual Timer 0/1
+0x1C0E_0000 0x1C0E_FFFF 64KB Dual Timer 2/3
+0x1C0F_0000 0x1C0F_FFFF 64KB DVI I2C
+0x1C10_0000 0x1C10_FFFF 64KB Real Time Clock
+0x1C11_0000 0x1C11_FFFF 64KB GPIO 0
+0x1C12_0000 0x1C12_FFFF 64KB GPIO 1
+0x1C13_0000 0x1C13_FFFF 64KB SCC
+0x1C14_0000 0x1C14_FFFF 64KB SMC configuration
+0x1C15_0000 0x1C15_FFFF 64KB I2S TX
+0x1D00_0000 0x1D0F_FFFF 1MB USB
+0x1D10_0000 0x1D1F_FFFF 1MB Ethernet
+0x1D20_0000 0x1D2F_FFFF 1MB SRAM
+0x1D30_0000 0x1FFF_FFFF 45MB Reserved 2
\ No newline at end of file
diff --git a/src/specs/morello/raw/input/morello/sdp_ap_sub_peripherals_map.csv b/src/specs/morello/raw/input/morello/sdp_ap_sub_peripherals_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..a216111d98792bf6d2da96e911f1362042f2366a
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_ap_sub_peripherals_map.csv
@@ -0,0 +1,57 @@
+0x00_2A00_0000 0x00_2A0F_FFFF 1MB Reserved
+0x00_2A10_0000 0x00_2A1F_FFFF 1MB Interconnect NIC GPV
+0x00_2A30_0000 0x00_2A3F_FFFF 1MB Reserved (Base NIC GPV)
+0x00_2A40_0000 0x00_2A40_FFFF 64KB APUART0
+0x00_2A41_0000 0x00_2A41_FFFF 64KB APUART1
+0x00_2A42_0000 0x00_2A42_FFFF 64KB System Security Control (SSC) registers
+0x00_2A43_0000 0x00_2A43_FFFF 64KB REFCLK CNTControl
+0x00_2A44_0000 0x00_2A44_FFFF 64KB Generic Watchdog Control
+0x00_2A45_0000 0x00_2A45_FFFF 64KB Generic Watchdog Refresh
+0x00_2A48_0000 0x00_2A48_FFFF 64KB Trusted Watchdog Control
+0x00_2A49_0000 0x00_2A49_FFFF 64KB Trusted Watchdog Refresh
+0x00_2A4B_0000 0x00_2A4B_FFFF 64KB AP_SEC_UART
+0x00_2A80_0000 0x00_2A80_FFFF 64KB REFCLK CNTRead
+0x00_2A81_0000 0x00_2A81_FFFF 64KB AP_REFCLK CNTCTL
+0x00_2A82_0000 0x00_2A82_FFFF 64KB AP_REFCLK_S CNTBase1
+0x00_2A83_0000 0x00_2A83_FFFF 64KB AP_REFCLK_NS CNTBase0
+0x00_2B00_0000 0x00_2B0F_FFFF 1MB Reserved for SCP/MCP to access whole of application space.
+0x00_2C00_0000 0x00_2C00_1FFF 8KB GICC registers
+0x00_2C01_0000 0x00_2C01_0FFF 4KB GICH registers
+0x00_2C02_0000 0x00_2C02_1FFF 8KB GICV registers
+0x00_2CC0_0000 0x00_2CDF_FFFF 2MB Display Controller
+0x00_2CE0_0000 0x00_2CEF_FFFF 1MB Display MMU
+0x00_2D00_0000 0x00_2DFF_FFFF 16MB GPU Element
+0x00_2E08_0000 0x00_2FFF_FFFF Invalid|32MB Reserved for GIC
+0x00_3000_0000 0x00_3000_FFFF 64KB GICD registers
+0x00_3001_0000 0x00_3001_FFFF 64KB GICA registers
+0x00_3002_0000 0x00_3002_FFFF 64KB GICT registers
+0x00_3003_0000 0x00_3003_FFFF 64KB GICP registers
+0x00_3004_0000 0x00_3004_FFFF 64KB GICITS0 ITS address
+0x00_3005_0000 0x00_3005_FFFF 64KB GICITS0 translater
+0x00_3006_0000 0x00_3006_FFFF 64KB GICITS1 address
+0x00_3007_0000 0x00_3007_FFFF 64KB GICITS1 translater
+0x00_3008_0000 0x00_3008_FFFF 64KB GICITS2 address
+0x00_3009_0000 0x00_3009_FFFF 64KB GICITS2 translater
+0x00_300A_0000 0x00_300A_FFFF 64KB GICITS3 address
+0x00_300B_0000 0x00_300B_FFFF 64KB GICITS3 translater
+0x00_300C_0000 0x00_300C_FFFF 64KB GICR registers
+0x00_4410_0000 0x00_4410_FFFF 64KB REFCLK general timer control
+0x00_4411_0000 0x00_4411_FFFF 64KB Cluster 0 time frame
+0x00_4412_0000 0x00_4412_FFFF 64KB Cluster 1 time frame
+0x00_4500_0000 0x00_4500_FFFF 64KB SCP Message Handling Unit (MHU) 0
+0x00_4501_0000 0x00_4501_FFFF 64KB SCP MHU1
+0x00_4520_0000 0x00_4521_FFFF 128KB SCP MHU Non-secure RAM
+0x00_4540_0000 0x00_4541_FFFF 128KB SCP MHU Secure RAM
+0x00_4700_0000 0x00_4700_FFFF 64KB SYSCNT_MSTSYN_CTRL
+0x00_4701_0000 0x00_4701_FFFF 64KB CSCNT_MSTSYNC_CTRL
+0x00_4C40_0000 0x00_4C40_FFFF 64KB AP2MCP MHU
+0x00_4C41_0000 0x00_4C41_FFFF 64KB AP2MCP MHU Non-Secure RAM
+0x00_4C42_0000 0x00_4C42_FFFF 64KB AP2MCP MHU Secure RAM
+0x00_4D00_0000 0x00_4DFF_FFFF 16MB Base STM
+0x00_4E00_0000 0x00_4EFF_FFFF 16MB Memory Element
+0x00_4F00_0000 0x00_4F03_FFFF 256KB Translation Control Unit (TCU) 0 for CCIX root port.
+0x00_4F04_0000 0x00_4F05_FFFF 128KB Translation Buffer Unit (TBU) 0 for CCIX root port.
+0x00_4F06_0000 0x00_4F07_FFFF 128KB Translation Buffer Unit (TBU) 1 for CCIX root port.
+0x00_4F40_0000 0x00_4F43_FFFF 256KB Translation Control Unit (TCU) 1 for PCIe root port.
+0x00_4F44_0000 0x00_4F45_FFFF 128KB Translation Buffer Unit (TBU) 0 for PCIe root port.
+0x00_4F46_0000 0x00_4F47_FFFF 128KB Translation Buffer Unit (TBU) 1 for PCIe root port.
\ No newline at end of file
diff --git a/src/specs/morello/raw/input/morello/sdp_mcp_map.csv b/src/specs/morello/raw/input/morello/sdp_mcp_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..4edf7565c5746cf555f5f067ce5e5d7b8e493a56
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_mcp_map.csv
@@ -0,0 +1,22 @@
+0x0_0000_0000 0x0_007F_FFFF 8MB Code boot ROM
+0x0_0080_0000 0x0_00FF_FFFF 8MB Code TCRAM
+0x0_0100_0000 0x0_15FF_FFFF 336MB Reserved part of MCP SoC expansion memory 1
+0x0_1600_0000 0x0_17FF_FFFF 32MB TMIF interface
+0x0_1800_0000 0x0_1FFF_FFFF 128MB Reserved part of MCP SoC expansion memory 2
+0x0_2000_0000 0x0_2001_FFFF 128KB SRAM DTCRAM
+0x0_2100_0000 0x0_2FFF_FFFF 240MB Reserved part of MCP SoC expansion memory 3
+0x0_3000_0000 0x0_33FF_FFFF 64MB MCP QSPI AHB
+0x0_3400_0000 0x0_3400_0FFF 4KB MCP QSPI APB
+0x0_3400_1000 0x0_3FFF_DFFF Invalid|191MB Reserved part of MCP SoC expansion memory 4
+0x0_3FFF_E000 0x0_3FFF_EFFF 4KB Reserved 1
+0x0_3FFF_F000 0x0_3FFF_FFFF 4KB MCP I2C 1 (BMC-PCC)
+0x0_4000_0000 0x0_43FF_FFFF 64MB MCP SoC expansion 1
+0x0_4560_0000 0x0_45FF_FFFF 10MB SCP2 MHU
+0x0_4800_0000 0x0_4BFF_FFFF 64MB MCP SoC expansion 2
+0x0_4C00_0000 0x0_4DFF_FFFF 32MB MCP peripherals
+0x0_5000_0000 0x0_507F_FFFF 8MB Element management peripherals
+0x0_6000_0000 0x0_9FFF_FFFF 1GB System Access Port. Translated to 0x0_4000_0000 to 0x0_7FFF_FFFF of AP memory map.
+0x0_A000_0000 0x0_DFFF_FFFF 1GB System Access Port. Translated to 0x0_0000_0000 to 0x0_3FFF_FFFF of AP memory map with debug address translation not enabled. Translated to 0x4_0000_0000 to 0x4_3FFF_FFFF of AP memory map with debug address translation enabled.
+0x0_E000_0000 0x0_E003_FFFF 256KB Private peripheral bus - Internal.
+0x0_E004_0000 0x0_E00F_FFFF 768KB Private peripheral bus - External.
+0x0_E010_0000 0x0_FFFF_FFFF 511MB Reserved 2
\ No newline at end of file
diff --git a/src/specs/morello/raw/input/morello/sdp_mcp_sub_peripherals_map.csv b/src/specs/morello/raw/input/morello/sdp_mcp_sub_peripherals_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..bfbe06081ee68c2868c634c20e033d5a66768432
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_mcp_sub_peripherals_map.csv
@@ -0,0 +1,8 @@
+0x00_4C00_0000 0x00_4C00_0FFF 4KB REFCLK CNTCTL
+0x00_4C00_1000 0x00_4C00_1FFF 4KB REFCLK CNTBase0
+0x00_4C00_2000 0x00_4C00_2FFF 4KB MCPUART0
+0x00_4C00_3000 0x00_4C00_3FFF 4KB MCPUART1
+0x00_4C00_6000 0x00_4C00_6FFF 4KB Watchdog (SP805)
+0x00_4C40_0000 0x00_4C40_FFFF 64KB AP2 MCP MHU
+0x00_4C41_0000 0x00_4C41_FFFF 64KB AP2 MCP MHU Non-secure RAM
+0x00_4C42_0000 0x00_4C42_FFFF 64KB AP2 MCP MHU Secure RAM
\ No newline at end of file
diff --git a/src/specs/morello/raw/input/morello/sdp_mcp_sub_peripherals_scp2_mhu_map.csv b/src/specs/morello/raw/input/morello/sdp_mcp_sub_peripherals_scp2_mhu_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..c5a21491950e36309f278f091651c821c5a9301d
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_mcp_sub_peripherals_scp2_mhu_map.csv
@@ -0,0 +1,3 @@
+0x00_4560_0000 0x00_4560_FFFF 64KB SCP2 MCP Message Handling Unit (MHU)
+0x00_4561_0000 0x00_4561_FFFF 64KB SCP2 MCP MHU Non-secure RAM
+0x00_4562_0000 0x00_4562_FFFF 64KB SCP2 MCP MHU Secure RAM
\ No newline at end of file
diff --git a/src/specs/morello/raw/input/morello/sdp_scp_map.csv b/src/specs/morello/raw/input/morello/sdp_scp_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..68a87919d1c7151f63c9b91941bd79faf29a5ea3
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_scp_map.csv
@@ -0,0 +1,32 @@
+0x0_0000_0000 0x0_007F_FFFF 8MB Code boot ROM
+0x0_0080_0000 0x0_00FF_FFFF 8MB Code TCRAM
+0x0_0100_0000 0x0_13FF_FFFF 304MB Reserved part of SCP SoC expansion memory 1
+0x0_1400_0000 0x0_15FF_FFFF 32MB TMIF interface
+0x0_1600_0000 0x0_1FFF_FFFF 160MB Reserved part of SCP SoC expansion memory 2
+0x0_2000_0000 0x0_2001_FFFF 128KB SRAM DTCRAM
+0x0_2100_0000 0x0_2FFF_FFFF 240MB Reserved part of SCP SoC expansion memory 3
+0x0_3000_0000 0x0_33FF_FFFF Invalid|64MB SCP QSPI AHB
+0x0_3400_0000 0x0_3400_0FFF Invalid|4KB SCP QSPI APB
+0x0_3400_1000 0x0_3FFD_FFFF Invalid|191MB Reserved part of SCP SoC expansion memory 4
+0x0_3FFE_0000 0x0_3FFE_FFFF 64KB SCP PVT CTRL
+0x0_3FFF_0000 0x0_3FFF_9FFF 40KB Reserved part of SCP SoC expansion memory 5
+0x0_3FFF_A000 0x0_3FFF_AFFF 4KB Reserved 1
+0x0_3FFF_B000 0x0_3FFF_BFFF 4KB SCP I2C1 (PMIC)
+0x0_3FFF_C000 0x0_3FFF_CFFF 4KB SCP I2C2 (SPD-PCC)
+0x0_3FFF_D000 0x0_3FFF_EFFF 8KB Reserved part of SCP SoC expansion memory 6
+0x0_3FFF_F000 0x0_3FFF_FFFF 4KB SCC registers
+0x0_4000_0000 0x0_43FF_FFFF 64MB SCP SoC expansion
+0x0_4400_0000 0x0_45FF_FFFF 32MB SCP peripherals
+0x0_4800_0000 0x0_4BFF_FFFF 64MB MCP SoC expansion
+0x0_4E00_0000 0x0_4E00_FFFF 64KB Memory element 0 configuration
+0x0_4E01_0000 0x0_4E01_FFFF 64KB Memory element 0 manager
+0x0_4E02_0000 0x0_4E02_FFFF 64KB Memory element 0 sensor group
+0x0_4E10_0000 0x0_4E10_FFFF 64KB Memory element 1 configuration
+0x0_4E11_0000 0x0_4E11_FFFF 64KB Memory element 1 manager
+0x0_4E12_0000 0x0_4E12_FFFF 64KB Memory element 1 sensor group
+0x0_5000_0000 0x0_507F_FFFF 8MB Element management peripherals
+0x0_6000_0000 0x0_9FFF_FFFF 1GB System Access Port. Translated to 0x0_4000_0000 to 0x0_7FFF_FFFF of AP memory map.
+0x0_A000_0000 0x0_DFFF_FFFF 1GB System Access Port. Translated to 0x0_0000_0000 to 0x0_3FFF_FFFF of AP memory map with debug address translation not enabled. Translated to 0x4_0000_0000 to 0x4_3FFF_FFFF of AP memory map with debug address translation enabled.
+0x0_E000_0000 0x0_E003_FFFF 256KB Private peripheral bus - Internal.
+0x0_E004_0000 0x0_E00F_FFFF 768KB Private peripheral bus - External.
+0x0_E010_0000 0x0_FFFF_FFFF 511MB Reserved 2
\ No newline at end of file
diff --git a/src/specs/morello/raw/input/morello/sdp_scp_sub_peripherals_map.csv b/src/specs/morello/raw/input/morello/sdp_scp_sub_peripherals_map.csv
new file mode 100644
index 0000000000000000000000000000000000000000..d5e379a35233bf824d6137c72586fd15ab375f1c
--- /dev/null
+++ b/src/specs/morello/raw/input/morello/sdp_scp_sub_peripherals_map.csv
@@ -0,0 +1,14 @@
+0x00_4400_0000 0x00_4400_0FFF 4KB REFCLK CNTCTL
+0x00_4400_1000 0x00_4400_1FFF 4KB REFCLK CNTBase0
+0x00_4400_2000 0x00_4400_2FFF 4KB SCPUART
+0x00_4400_6000 0x00_4400_6FFF 4KB Watchdog (SP805)
+0x00_4400_A000 0x00_4400_AFFF 4KB CS CNTCONTROL
+0x00_4410_0000 0x00_4410_FFFF 64KB REFCLK general timer control
+0x00_4411_0000 0x00_4411_FFFF 64KB Cluster 0 time frame
+0x00_4412_0000 0x00_4412_FFFF 64KB Cluster 1 time frame
+0x00_4500_0000 0x00_4501_FFFF 128KB AP2SCP Message Handling Unit (MHU)
+0x00_452C_0000 0x00_452D_FFFF 128KB AP2SCP MHU Non-secure RAM
+0x00_4540_0000 0x00_4541_FFFF 128KB AP2SCP MHU Secure RAM
+0x00_4560_0000 0x00_4560_FFFF 64KB SCP2MCH MHU
+0x00_4561_0000 0x00_4561_FFFF 64KB SCP2MCP MHU Non-secure RAM
+0x00_4562_0000 0x00_4562_FFFF 64KB SCP2MCP MHU Secure RAM
\ No newline at end of file
diff --git a/src/specs/morello/raw/transform.py b/src/specs/morello/raw/transform.py
new file mode 100644
index 0000000000000000000000000000000000000000..67a0e590f444570082c94842dc751af7dd260c28
--- /dev/null
+++ b/src/specs/morello/raw/transform.py
@@ -0,0 +1,121 @@
+import os
+import csv
+
+directory = "input"
+
+parent_asid_var = "subsystem_peripherals"
+parent_off = "off"
+
+output_base_dir = "../src/specs/"
+
+
+def write_struct(
+    file, line_prefix, struct_type_name, name, description, start, end, length
+):
+
+    factor = length[:-2]
+    unit = length[-2:]
+
+    file.write(line_prefix + struct_type_name + " {\n")
+
+    if "Invalid" in length:
+        file.write(line_prefix + "    // Length: " + length + "\n")
+        length = None
+
+    file.write(line_prefix + '    name: "' + name + '",\n')
+    file.write(line_prefix + '    description: "' + description + '",\n')
+    file.write(line_prefix + "    start: ")
+    if start is not None:
+        file.write("Some(0x" + start.to_bytes(6, "big").hex("_", 2) + ")")
+    else:
+        file.write("None")
+    file.write(",\n")
+    file.write(line_prefix + "    end: ")
+    if end is not None:
+        file.write("Some(0x" + end.to_bytes(6, "big").hex("_", 2) + ")")
+    else:
+        file.write("None")
+    file.write(",\n")
+    file.write(line_prefix + "    length: ")
+    if length is not None:
+        file.write("Some(size!(" + factor + ", " + unit + "))")
+    else:
+        file.write("None")
+    file.write(",\n")
+
+    file.write(line_prefix + "}")
+
+
+for target in os.listdir("input"):
+
+    if not os.path.isdir(output_base_dir + target):
+        os.makedirs(output_base_dir + target)
+
+    for file in os.listdir("input/" + target):
+
+        with open("input" + "/" + target + "/" + file) as csvfile:
+
+            reader = csv.reader(csvfile, delimiter=" ")
+
+            outfilename = file
+            outfilename = outfilename.replace(".csv", ".rs")
+
+            outfile_path = output_base_dir + target + "/" + outfilename
+            print("Transforming " + file + " > " + outfile_path)
+
+            array_name = "MAPINFOS"
+
+            struct_type_name = "MapInfo"
+
+            with open(outfile_path, "w+") as outfile:
+
+                outfile.write("use crate::specs::common::*;\n")
+                outfile.write("\n")
+                outfile.write(
+                    "pub static "
+                    + array_name.upper()
+                    + ": &'static ["
+                    + struct_type_name
+                    + "] = &[\n"
+                )
+
+                for idx, row in enumerate(reader):
+
+                    print(" Row " + str(idx), end="\r")
+
+                    start_address = int(row[0], base=0)
+                    end_address = int(row[1], base=0)
+                    length = row[2]
+                    name = ""
+                    for i in range(3, len(row)):
+                        if name != "":
+                            name = name + " "
+                        name = name + row[i]
+
+                    variable_name = name.lower()
+                    variable_name = variable_name.replace("(", "")
+                    variable_name = variable_name.replace(")", "")
+                    variable_name = variable_name.replace(".", "")
+                    variable_name = variable_name.replace(",", "_")
+                    variable_name = variable_name.replace("/", "_")
+                    variable_name = variable_name.replace(" ", "_")
+                    variable_name = variable_name.replace("-", "_")
+
+                    # print("let " + variable_name + " = add_sub_as_len(dn, " + parent_asid_var + ", " + parent_off + ", \"" + name + "\", \"\", " + address_string + ", size!(" + size + "))?;")
+
+                    # Adding one to end address, because our ranges are [start, end) not [start, end]
+                    write_struct(
+                        file=outfile,
+                        line_prefix="    ",
+                        struct_type_name=struct_type_name,
+                        name=variable_name,
+                        description=name,
+                        start=start_address,
+                        end=end_address + 1,
+                        length=length,
+                    )
+                    outfile.write(",\n")
+
+                outfile.write("];\n")
+
+            print("")
diff --git a/src/specs/morello/sdp_ap_map.rs b/src/specs/morello/sdp_ap_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..844b8923b40ab8f924d9e9ab5bf6ba2fe6140654
--- /dev/null
+++ b/src/specs/morello/sdp_ap_map.rs
@@ -0,0 +1,152 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "boot_region",
+        description: "Boot region",
+        start: Some(0x0000_0000_0000),
+        end: Some(0x0000_0800_0000),
+        length: Some(size!(128, MB)),
+    },
+    MapInfo {
+        // Length: Invalid|Custom|NotCalculated
+        name: "iofpga_map_window",
+        description: "IOFPGA Map Window",
+        start: Some(0x0000_0800_0000),
+        end: Some(0x0000_2000_0000),
+        length: None,
+    },
+    MapInfo {
+        name: "pcie_rc_apb",
+        description: "PCIe rc (APB)",
+        start: Some(0x0000_2000_0000),
+        end: Some(0x0000_2080_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "pcie_phy_apb",
+        description: "PCIe PHY (APB)",
+        start: Some(0x0000_2080_0000),
+        end: Some(0x0000_2085_0000),
+        length: Some(size!(320, KB)),
+    },
+    MapInfo {
+        name: "pcie_msg_apb",
+        description: "PCIe msg (APB)",
+        start: Some(0x0000_2085_0000),
+        end: Some(0x0000_2086_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ccix_rc_apb",
+        description: "CCIX rc (APB)",
+        start: Some(0x0000_2100_0000),
+        end: Some(0x0000_2180_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "ccix_phy_apb",
+        description: "CCIX PHY (APB)",
+        start: Some(0x0000_2180_0000),
+        end: Some(0x0000_2185_0000),
+        length: Some(size!(320, KB)),
+    },
+    MapInfo {
+        name: "ccix_msg_apb",
+        description: "CCIX msg (APB)",
+        start: Some(0x0000_2185_0000),
+        end: Some(0x0000_2186_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "nic_400_soc_gpv",
+        description: "NIC-400 SoC GPV",
+        start: Some(0x0000_2200_0000),
+        end: Some(0x0000_2210_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "gpio",
+        description: "GPIO",
+        start: Some(0x0000_2210_0000),
+        end: Some(0x0000_2211_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "subsystem_peripherals",
+        description: "Subsystem peripherals",
+        start: Some(0x0000_2a00_0000),
+        end: Some(0x0000_5000_0000),
+        length: Some(size!(608, MB)),
+    },
+    MapInfo {
+        name: "cmn_skeena_gpv",
+        description: "CMN-Skeena GPV",
+        start: Some(0x0000_5000_0000),
+        end: Some(0x0000_6000_0000),
+        length: Some(size!(256, MB)),
+    },
+    MapInfo {
+        name: "pcie_subordinate_axi_1",
+        description: "PCIe subordinate AXI 1",
+        start: Some(0x0000_6000_0000),
+        end: Some(0x0000_7000_0000),
+        length: Some(size!(256, MB)),
+    },
+    MapInfo {
+        name: "ccix_subordinate_axi_1",
+        description: "CCIX subordinate AXI 1",
+        start: Some(0x0000_7000_0000),
+        end: Some(0x0000_8000_0000),
+        length: Some(size!(256, MB)),
+    },
+    MapInfo {
+        name: "dram0",
+        description: "DRAM0",
+        start: Some(0x0000_8000_0000),
+        end: Some(0x0001_0000_0000),
+        length: Some(size!(2, GB)),
+    },
+    MapInfo {
+        name: "coresight_subsystem",
+        description: "CoreSight subsystem",
+        start: Some(0x0004_0000_0000),
+        end: Some(0x0005_0000_0000),
+        length: Some(size!(4, GB)),
+    },
+    MapInfo {
+        name: "iofpga_tlx_manager_if",
+        description: "IOFPGA TLX manager IF",
+        start: Some(0x0005_0000_0000),
+        end: Some(0x0009_0000_0000),
+        length: Some(size!(16, GB)),
+    },
+    MapInfo {
+        name: "pcie_subordinate_axi_2",
+        description: "PCIe subordinate AXI 2",
+        start: Some(0x0009_0000_0000),
+        end: Some(0x0029_0000_0000),
+        length: Some(size!(128, GB)),
+    },
+    MapInfo {
+        name: "ccix_subordinate_axi_2",
+        description: "CCIX subordinate AXI 2",
+        start: Some(0x0030_0000_0000),
+        end: Some(0x0050_0000_0000),
+        length: Some(size!(128, GB)),
+    },
+    MapInfo {
+        name: "dram1",
+        description: "DRAM1",
+        start: Some(0x0080_8000_0000),
+        end: Some(0x0100_0000_0000),
+        length: Some(size!(510, GB)),
+    },
+    MapInfo {
+        name: "dram2",
+        description: "DRAM2",
+        start: Some(0x0100_0000_0000),
+        end: Some(0x0400_0000_0000),
+        length: Some(size!(3, TB)),
+    },
+];
diff --git a/src/specs/morello/sdp_ap_sub_iofpga_map.rs b/src/specs/morello/sdp_ap_sub_iofpga_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c082fd09575785f394e7b9d961c4da77665eac9c
--- /dev/null
+++ b/src/specs/morello/sdp_ap_sub_iofpga_map.rs
@@ -0,0 +1,221 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "ddr3",
+        description: "DDR3",
+        start: Some(0x0000_0800_0000),
+        end: Some(0x0000_1400_0000),
+        length: Some(size!(192, MB)),
+    },
+    MapInfo {
+        name: "scp_backup_boot_memory",
+        description: "SCP backup boot memory",
+        start: Some(0x0000_1400_0000),
+        end: Some(0x0000_1600_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "mcp_backup_boot_memory",
+        description: "MCP backup boot memory",
+        start: Some(0x0000_1600_0000),
+        end: Some(0x0000_1800_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "qspi_scp_mcp",
+        description: "QSPI (SCP/MCP)",
+        start: Some(0x0000_1800_0000),
+        end: Some(0x0000_1a00_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "qspi_ap",
+        description: "QSPI (AP)",
+        start: Some(0x0000_1a00_0000),
+        end: Some(0x0000_1c00_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "i2s",
+        description: "I2S",
+        start: Some(0x0000_1c00_0000),
+        end: Some(0x0000_1c01_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "system_registers",
+        description: "System registers",
+        start: Some(0x0000_1c01_0000),
+        end: Some(0x0000_1c02_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "system_control",
+        description: "System control",
+        start: Some(0x0000_1c02_0000),
+        end: Some(0x0000_1c03_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "pcie_sw_i2c",
+        description: "PCIe SW I2C",
+        start: Some(0x0000_1c03_0000),
+        end: Some(0x0000_1c04_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "soc_ddr_spd",
+        description: "SoC DDR SPD",
+        start: Some(0x0000_1c04_0000),
+        end: Some(0x0000_1c05_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "qspi_configuration_ap",
+        description: "QSPI configuration (AP)",
+        start: Some(0x0000_1c05_0000),
+        end: Some(0x0000_1c06_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "sdio_microsd_configuration",
+        description: "SDIO microSD configuration",
+        start: Some(0x0000_1c06_0000),
+        end: Some(0x0000_1c07_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "sdio_emmc_configuration",
+        description: "SDIO eMMC configuration",
+        start: Some(0x0000_1c07_0000),
+        end: Some(0x0000_1c08_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "reserved_1",
+        description: "Reserved 1",
+        start: Some(0x0000_1c08_0000),
+        end: Some(0x0000_1c09_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "fpgauart0",
+        description: "FPGAUART0",
+        start: Some(0x0000_1c09_0000),
+        end: Some(0x0000_1c0a_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "fpgauart1",
+        description: "FPGAUART1",
+        start: Some(0x0000_1c0a_0000),
+        end: Some(0x0000_1c0b_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "watchdog",
+        description: "Watchdog",
+        start: Some(0x0000_1c0b_0000),
+        end: Some(0x0000_1c0c_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "qspi_spi_configuration_ap",
+        description: "QSPI SPI configuration (AP)",
+        start: Some(0x0000_1c0c_0000),
+        end: Some(0x0000_1c0d_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "dual_timer_0_1",
+        description: "Dual Timer 0/1",
+        start: Some(0x0000_1c0d_0000),
+        end: Some(0x0000_1c0e_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "dual_timer_2_3",
+        description: "Dual Timer 2/3",
+        start: Some(0x0000_1c0e_0000),
+        end: Some(0x0000_1c0f_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "dvi_i2c",
+        description: "DVI I2C",
+        start: Some(0x0000_1c0f_0000),
+        end: Some(0x0000_1c10_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "real_time_clock",
+        description: "Real Time Clock",
+        start: Some(0x0000_1c10_0000),
+        end: Some(0x0000_1c11_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gpio_0",
+        description: "GPIO 0",
+        start: Some(0x0000_1c11_0000),
+        end: Some(0x0000_1c12_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gpio_1",
+        description: "GPIO 1",
+        start: Some(0x0000_1c12_0000),
+        end: Some(0x0000_1c13_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scc",
+        description: "SCC",
+        start: Some(0x0000_1c13_0000),
+        end: Some(0x0000_1c14_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "smc_configuration",
+        description: "SMC configuration",
+        start: Some(0x0000_1c14_0000),
+        end: Some(0x0000_1c15_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "i2s_tx",
+        description: "I2S TX",
+        start: Some(0x0000_1c15_0000),
+        end: Some(0x0000_1c16_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "usb",
+        description: "USB",
+        start: Some(0x0000_1d00_0000),
+        end: Some(0x0000_1d10_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "ethernet",
+        description: "Ethernet",
+        start: Some(0x0000_1d10_0000),
+        end: Some(0x0000_1d20_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "sram",
+        description: "SRAM",
+        start: Some(0x0000_1d20_0000),
+        end: Some(0x0000_1d30_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "reserved_2",
+        description: "Reserved 2",
+        start: Some(0x0000_1d30_0000),
+        end: Some(0x0000_2000_0000),
+        length: Some(size!(45, MB)),
+    },
+];
diff --git a/src/specs/morello/sdp_ap_sub_peripherals_map.rs b/src/specs/morello/sdp_ap_sub_peripherals_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..59a9df8531ada74f3ddac5c2c32183ac4de138dd
--- /dev/null
+++ b/src/specs/morello/sdp_ap_sub_peripherals_map.rs
@@ -0,0 +1,404 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "reserved",
+        description: "Reserved",
+        start: Some(0x0000_2a00_0000),
+        end: Some(0x0000_2a10_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "interconnect_nic_gpv",
+        description: "Interconnect NIC GPV",
+        start: Some(0x0000_2a10_0000),
+        end: Some(0x0000_2a20_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "reserved_base_nic_gpv",
+        description: "Reserved (Base NIC GPV)",
+        start: Some(0x0000_2a30_0000),
+        end: Some(0x0000_2a40_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "apuart0",
+        description: "APUART0",
+        start: Some(0x0000_2a40_0000),
+        end: Some(0x0000_2a41_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "apuart1",
+        description: "APUART1",
+        start: Some(0x0000_2a41_0000),
+        end: Some(0x0000_2a42_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "system_security_control_ssc_registers",
+        description: "System Security Control (SSC) registers",
+        start: Some(0x0000_2a42_0000),
+        end: Some(0x0000_2a43_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "refclk_cntcontrol",
+        description: "REFCLK CNTControl",
+        start: Some(0x0000_2a43_0000),
+        end: Some(0x0000_2a44_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "generic_watchdog_control",
+        description: "Generic Watchdog Control",
+        start: Some(0x0000_2a44_0000),
+        end: Some(0x0000_2a45_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "generic_watchdog_refresh",
+        description: "Generic Watchdog Refresh",
+        start: Some(0x0000_2a45_0000),
+        end: Some(0x0000_2a46_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "trusted_watchdog_control",
+        description: "Trusted Watchdog Control",
+        start: Some(0x0000_2a48_0000),
+        end: Some(0x0000_2a49_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "trusted_watchdog_refresh",
+        description: "Trusted Watchdog Refresh",
+        start: Some(0x0000_2a49_0000),
+        end: Some(0x0000_2a4a_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap_sec_uart",
+        description: "AP_SEC_UART",
+        start: Some(0x0000_2a4b_0000),
+        end: Some(0x0000_2a4c_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "refclk_cntread",
+        description: "REFCLK CNTRead",
+        start: Some(0x0000_2a80_0000),
+        end: Some(0x0000_2a81_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap_refclk_cntctl",
+        description: "AP_REFCLK CNTCTL",
+        start: Some(0x0000_2a81_0000),
+        end: Some(0x0000_2a82_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap_refclk_s_cntbase1",
+        description: "AP_REFCLK_S CNTBase1",
+        start: Some(0x0000_2a82_0000),
+        end: Some(0x0000_2a83_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap_refclk_ns_cntbase0",
+        description: "AP_REFCLK_NS CNTBase0",
+        start: Some(0x0000_2a83_0000),
+        end: Some(0x0000_2a84_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "reserved_for_scp_mcp_to_access_whole_of_application_space",
+        description: "Reserved for SCP/MCP to access whole of application space.",
+        start: Some(0x0000_2b00_0000),
+        end: Some(0x0000_2b10_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "gicc_registers",
+        description: "GICC registers",
+        start: Some(0x0000_2c00_0000),
+        end: Some(0x0000_2c00_2000),
+        length: Some(size!(8, KB)),
+    },
+    MapInfo {
+        name: "gich_registers",
+        description: "GICH registers",
+        start: Some(0x0000_2c01_0000),
+        end: Some(0x0000_2c01_1000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "gicv_registers",
+        description: "GICV registers",
+        start: Some(0x0000_2c02_0000),
+        end: Some(0x0000_2c02_2000),
+        length: Some(size!(8, KB)),
+    },
+    MapInfo {
+        name: "display_controller",
+        description: "Display Controller",
+        start: Some(0x0000_2cc0_0000),
+        end: Some(0x0000_2ce0_0000),
+        length: Some(size!(2, MB)),
+    },
+    MapInfo {
+        name: "display_mmu",
+        description: "Display MMU",
+        start: Some(0x0000_2ce0_0000),
+        end: Some(0x0000_2cf0_0000),
+        length: Some(size!(1, MB)),
+    },
+    MapInfo {
+        name: "gpu_element",
+        description: "GPU Element",
+        start: Some(0x0000_2d00_0000),
+        end: Some(0x0000_2e00_0000),
+        length: Some(size!(16, MB)),
+    },
+    MapInfo {
+        // Length: Invalid|32MB
+        name: "reserved_for_gic",
+        description: "Reserved for GIC",
+        start: Some(0x0000_2e08_0000),
+        end: Some(0x0000_3000_0000),
+        length: None,
+    },
+    MapInfo {
+        name: "gicd_registers",
+        description: "GICD registers",
+        start: Some(0x0000_3000_0000),
+        end: Some(0x0000_3001_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gica_registers",
+        description: "GICA registers",
+        start: Some(0x0000_3001_0000),
+        end: Some(0x0000_3002_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gict_registers",
+        description: "GICT registers",
+        start: Some(0x0000_3002_0000),
+        end: Some(0x0000_3003_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicp_registers",
+        description: "GICP registers",
+        start: Some(0x0000_3003_0000),
+        end: Some(0x0000_3004_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits0_its_address",
+        description: "GICITS0 ITS address",
+        start: Some(0x0000_3004_0000),
+        end: Some(0x0000_3005_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits0_translater",
+        description: "GICITS0 translater",
+        start: Some(0x0000_3005_0000),
+        end: Some(0x0000_3006_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits1_address",
+        description: "GICITS1 address",
+        start: Some(0x0000_3006_0000),
+        end: Some(0x0000_3007_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits1_translater",
+        description: "GICITS1 translater",
+        start: Some(0x0000_3007_0000),
+        end: Some(0x0000_3008_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits2_address",
+        description: "GICITS2 address",
+        start: Some(0x0000_3008_0000),
+        end: Some(0x0000_3009_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits2_translater",
+        description: "GICITS2 translater",
+        start: Some(0x0000_3009_0000),
+        end: Some(0x0000_300a_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits3_address",
+        description: "GICITS3 address",
+        start: Some(0x0000_300a_0000),
+        end: Some(0x0000_300b_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicits3_translater",
+        description: "GICITS3 translater",
+        start: Some(0x0000_300b_0000),
+        end: Some(0x0000_300c_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "gicr_registers",
+        description: "GICR registers",
+        start: Some(0x0000_300c_0000),
+        end: Some(0x0000_300d_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "refclk_general_timer_control",
+        description: "REFCLK general timer control",
+        start: Some(0x0000_4410_0000),
+        end: Some(0x0000_4411_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "cluster_0_time_frame",
+        description: "Cluster 0 time frame",
+        start: Some(0x0000_4411_0000),
+        end: Some(0x0000_4412_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "cluster_1_time_frame",
+        description: "Cluster 1 time frame",
+        start: Some(0x0000_4412_0000),
+        end: Some(0x0000_4413_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp_message_handling_unit_mhu_0",
+        description: "SCP Message Handling Unit (MHU) 0",
+        start: Some(0x0000_4500_0000),
+        end: Some(0x0000_4501_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp_mhu1",
+        description: "SCP MHU1",
+        start: Some(0x0000_4501_0000),
+        end: Some(0x0000_4502_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp_mhu_non_secure_ram",
+        description: "SCP MHU Non-secure RAM",
+        start: Some(0x0000_4520_0000),
+        end: Some(0x0000_4522_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "scp_mhu_secure_ram",
+        description: "SCP MHU Secure RAM",
+        start: Some(0x0000_4540_0000),
+        end: Some(0x0000_4542_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "syscnt_mstsyn_ctrl",
+        description: "SYSCNT_MSTSYN_CTRL",
+        start: Some(0x0000_4700_0000),
+        end: Some(0x0000_4701_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "cscnt_mstsync_ctrl",
+        description: "CSCNT_MSTSYNC_CTRL",
+        start: Some(0x0000_4701_0000),
+        end: Some(0x0000_4702_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap2mcp_mhu",
+        description: "AP2MCP MHU",
+        start: Some(0x0000_4c40_0000),
+        end: Some(0x0000_4c41_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap2mcp_mhu_non_secure_ram",
+        description: "AP2MCP MHU Non-Secure RAM",
+        start: Some(0x0000_4c41_0000),
+        end: Some(0x0000_4c42_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap2mcp_mhu_secure_ram",
+        description: "AP2MCP MHU Secure RAM",
+        start: Some(0x0000_4c42_0000),
+        end: Some(0x0000_4c43_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "base_stm",
+        description: "Base STM",
+        start: Some(0x0000_4d00_0000),
+        end: Some(0x0000_4e00_0000),
+        length: Some(size!(16, MB)),
+    },
+    MapInfo {
+        name: "memory_element",
+        description: "Memory Element",
+        start: Some(0x0000_4e00_0000),
+        end: Some(0x0000_4f00_0000),
+        length: Some(size!(16, MB)),
+    },
+    MapInfo {
+        name: "translation_control_unit_tcu_0_for_ccix_root_port",
+        description: "Translation Control Unit (TCU) 0 for CCIX root port.",
+        start: Some(0x0000_4f00_0000),
+        end: Some(0x0000_4f04_0000),
+        length: Some(size!(256, KB)),
+    },
+    MapInfo {
+        name: "translation_buffer_unit_tbu_0_for_ccix_root_port",
+        description: "Translation Buffer Unit (TBU) 0 for CCIX root port.",
+        start: Some(0x0000_4f04_0000),
+        end: Some(0x0000_4f06_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "translation_buffer_unit_tbu_1_for_ccix_root_port",
+        description: "Translation Buffer Unit (TBU) 1 for CCIX root port.",
+        start: Some(0x0000_4f06_0000),
+        end: Some(0x0000_4f08_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "translation_control_unit_tcu_1_for_pcie_root_port",
+        description: "Translation Control Unit (TCU) 1 for PCIe root port.",
+        start: Some(0x0000_4f40_0000),
+        end: Some(0x0000_4f44_0000),
+        length: Some(size!(256, KB)),
+    },
+    MapInfo {
+        name: "translation_buffer_unit_tbu_0_for_pcie_root_port",
+        description: "Translation Buffer Unit (TBU) 0 for PCIe root port.",
+        start: Some(0x0000_4f44_0000),
+        end: Some(0x0000_4f46_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "translation_buffer_unit_tbu_1_for_pcie_root_port",
+        description: "Translation Buffer Unit (TBU) 1 for PCIe root port.",
+        start: Some(0x0000_4f46_0000),
+        end: Some(0x0000_4f48_0000),
+        length: Some(size!(128, KB)),
+    },
+];
diff --git a/src/specs/morello/sdp_mcp_map.rs b/src/specs/morello/sdp_mcp_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d4e1981a77aca12bcf46a3aafdb539e085c9ea3f
--- /dev/null
+++ b/src/specs/morello/sdp_mcp_map.rs
@@ -0,0 +1,159 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "code_boot_rom",
+        description: "Code boot ROM",
+        start: Some(0x0000_0000_0000),
+        end: Some(0x0000_0080_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "code_tcram",
+        description: "Code TCRAM",
+        start: Some(0x0000_0080_0000),
+        end: Some(0x0000_0100_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_mcp_soc_expansion_memory_1",
+        description: "Reserved part of MCP SoC expansion memory 1",
+        start: Some(0x0000_0100_0000),
+        end: Some(0x0000_1600_0000),
+        length: Some(size!(336, MB)),
+    },
+    MapInfo {
+        name: "tmif_interface",
+        description: "TMIF interface",
+        start: Some(0x0000_1600_0000),
+        end: Some(0x0000_1800_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_mcp_soc_expansion_memory_2",
+        description: "Reserved part of MCP SoC expansion memory 2",
+        start: Some(0x0000_1800_0000),
+        end: Some(0x0000_2000_0000),
+        length: Some(size!(128, MB)),
+    },
+    MapInfo {
+        name: "sram_dtcram",
+        description: "SRAM DTCRAM",
+        start: Some(0x0000_2000_0000),
+        end: Some(0x0000_2002_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_mcp_soc_expansion_memory_3",
+        description: "Reserved part of MCP SoC expansion memory 3",
+        start: Some(0x0000_2100_0000),
+        end: Some(0x0000_3000_0000),
+        length: Some(size!(240, MB)),
+    },
+    MapInfo {
+        name: "mcp_qspi_ahb",
+        description: "MCP QSPI AHB",
+        start: Some(0x0000_3000_0000),
+        end: Some(0x0000_3400_0000),
+        length: Some(size!(64, MB)),
+    },
+    MapInfo {
+        name: "mcp_qspi_apb",
+        description: "MCP QSPI APB",
+        start: Some(0x0000_3400_0000),
+        end: Some(0x0000_3400_1000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        // Length: Invalid|191MB
+        name: "reserved_part_of_mcp_soc_expansion_memory_4",
+        description: "Reserved part of MCP SoC expansion memory 4",
+        start: Some(0x0000_3400_1000),
+        end: Some(0x0000_3fff_e000),
+        length: None,
+    },
+    MapInfo {
+        name: "reserved_1",
+        description: "Reserved 1",
+        start: Some(0x0000_3fff_e000),
+        end: Some(0x0000_3fff_f000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "mcp_i2c_1_bmc_pcc",
+        description: "MCP I2C 1 (BMC-PCC)",
+        start: Some(0x0000_3fff_f000),
+        end: Some(0x0000_4000_0000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "mcp_soc_expansion_1",
+        description: "MCP SoC expansion 1",
+        start: Some(0x0000_4000_0000),
+        end: Some(0x0000_4400_0000),
+        length: Some(size!(64, MB)),
+    },
+    MapInfo {
+        name: "scp2_mhu",
+        description: "SCP2 MHU",
+        start: Some(0x0000_4560_0000),
+        end: Some(0x0000_4600_0000),
+        length: Some(size!(10, MB)),
+    },
+    MapInfo {
+        name: "mcp_soc_expansion_2",
+        description: "MCP SoC expansion 2",
+        start: Some(0x0000_4800_0000),
+        end: Some(0x0000_4c00_0000),
+        length: Some(size!(64, MB)),
+    },
+    MapInfo {
+        name: "mcp_peripherals",
+        description: "MCP peripherals",
+        start: Some(0x0000_4c00_0000),
+        end: Some(0x0000_4e00_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "element_management_peripherals",
+        description: "Element management peripherals",
+        start: Some(0x0000_5000_0000),
+        end: Some(0x0000_5080_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "system_access_port_translated_to_0x0_4000_0000_to_0x0_7fff_ffff_of_ap_memory_map",
+        description: "System Access Port. Translated to 0x0_4000_0000 to 0x0_7FFF_FFFF of AP memory map.",
+        start: Some(0x0000_6000_0000),
+        end: Some(0x0000_a000_0000),
+        length: Some(size!(1, GB)),
+    },
+    MapInfo {
+        name: "system_access_port_translated_to_0x0_0000_0000_to_0x0_3fff_ffff_of_ap_memory_map_with_debug_address_translation_not_enabled_translated_to_0x4_0000_0000_to_0x4_3fff_ffff_of_ap_memory_map_with_debug_address_translation_enabled",
+        description: "System Access Port. Translated to 0x0_0000_0000 to 0x0_3FFF_FFFF of AP memory map with debug address translation not enabled. Translated to 0x4_0000_0000 to 0x4_3FFF_FFFF of AP memory map with debug address translation enabled.",
+        start: Some(0x0000_a000_0000),
+        end: Some(0x0000_e000_0000),
+        length: Some(size!(1, GB)),
+    },
+    MapInfo {
+        name: "private_peripheral_bus___internal",
+        description: "Private peripheral bus - Internal.",
+        start: Some(0x0000_e000_0000),
+        end: Some(0x0000_e004_0000),
+        length: Some(size!(256, KB)),
+    },
+    MapInfo {
+        name: "private_peripheral_bus___external",
+        description: "Private peripheral bus - External.",
+        start: Some(0x0000_e004_0000),
+        end: Some(0x0000_e010_0000),
+        length: Some(size!(768, KB)),
+    },
+    MapInfo {
+        name: "reserved_2",
+        description: "Reserved 2",
+        start: Some(0x0000_e010_0000),
+        end: Some(0x0001_0000_0000),
+        length: Some(size!(511, MB)),
+    },
+];
diff --git a/src/specs/morello/sdp_mcp_sub_peripherals_map.rs b/src/specs/morello/sdp_mcp_sub_peripherals_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a03752810d9abcac57ea8ac87361b3c958b2e045
--- /dev/null
+++ b/src/specs/morello/sdp_mcp_sub_peripherals_map.rs
@@ -0,0 +1,60 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "refclk_cntctl",
+        description: "REFCLK CNTCTL",
+        start: Some(0x0000_4c00_0000),
+        end: Some(0x0000_4c00_1000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "refclk_cntbase0",
+        description: "REFCLK CNTBase0",
+        start: Some(0x0000_4c00_1000),
+        end: Some(0x0000_4c00_2000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "mcpuart0",
+        description: "MCPUART0",
+        start: Some(0x0000_4c00_2000),
+        end: Some(0x0000_4c00_3000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "mcpuart1",
+        description: "MCPUART1",
+        start: Some(0x0000_4c00_3000),
+        end: Some(0x0000_4c00_4000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "watchdog_sp805",
+        description: "Watchdog (SP805)",
+        start: Some(0x0000_4c00_6000),
+        end: Some(0x0000_4c00_7000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "ap2_mcp_mhu",
+        description: "AP2 MCP MHU",
+        start: Some(0x0000_4c40_0000),
+        end: Some(0x0000_4c41_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap2_mcp_mhu_non_secure_ram",
+        description: "AP2 MCP MHU Non-secure RAM",
+        start: Some(0x0000_4c41_0000),
+        end: Some(0x0000_4c42_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap2_mcp_mhu_secure_ram",
+        description: "AP2 MCP MHU Secure RAM",
+        start: Some(0x0000_4c42_0000),
+        end: Some(0x0000_4c43_0000),
+        length: Some(size!(64, KB)),
+    },
+];
diff --git a/src/specs/morello/sdp_mcp_sub_peripherals_scp2_mhu_map.rs b/src/specs/morello/sdp_mcp_sub_peripherals_scp2_mhu_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..499ce459bc7ae1e6031bf68d7e2635ae22a4a5b9
--- /dev/null
+++ b/src/specs/morello/sdp_mcp_sub_peripherals_scp2_mhu_map.rs
@@ -0,0 +1,25 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "scp2_mcp_message_handling_unit_mhu",
+        description: "SCP2 MCP Message Handling Unit (MHU)",
+        start: Some(0x0000_4560_0000),
+        end: Some(0x0000_4561_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp2_mcp_mhu_non_secure_ram",
+        description: "SCP2 MCP MHU Non-secure RAM",
+        start: Some(0x0000_4561_0000),
+        end: Some(0x0000_4562_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp2_mcp_mhu_secure_ram",
+        description: "SCP2 MCP MHU Secure RAM",
+        start: Some(0x0000_4562_0000),
+        end: Some(0x0000_4563_0000),
+        length: Some(size!(64, KB)),
+    },
+];
diff --git a/src/specs/morello/sdp_scp_map.rs b/src/specs/morello/sdp_scp_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ad740f0ec94c6b17c0b45b918777a70e61900acb
--- /dev/null
+++ b/src/specs/morello/sdp_scp_map.rs
@@ -0,0 +1,231 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "code_boot_rom",
+        description: "Code boot ROM",
+        start: Some(0x0000_0000_0000),
+        end: Some(0x0000_0080_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "code_tcram",
+        description: "Code TCRAM",
+        start: Some(0x0000_0080_0000),
+        end: Some(0x0000_0100_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_scp_soc_expansion_memory_1",
+        description: "Reserved part of SCP SoC expansion memory 1",
+        start: Some(0x0000_0100_0000),
+        end: Some(0x0000_1400_0000),
+        length: Some(size!(304, MB)),
+    },
+    MapInfo {
+        name: "tmif_interface",
+        description: "TMIF interface",
+        start: Some(0x0000_1400_0000),
+        end: Some(0x0000_1600_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_scp_soc_expansion_memory_2",
+        description: "Reserved part of SCP SoC expansion memory 2",
+        start: Some(0x0000_1600_0000),
+        end: Some(0x0000_2000_0000),
+        length: Some(size!(160, MB)),
+    },
+    MapInfo {
+        name: "sram_dtcram",
+        description: "SRAM DTCRAM",
+        start: Some(0x0000_2000_0000),
+        end: Some(0x0000_2002_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_scp_soc_expansion_memory_3",
+        description: "Reserved part of SCP SoC expansion memory 3",
+        start: Some(0x0000_2100_0000),
+        end: Some(0x0000_3000_0000),
+        length: Some(size!(240, MB)),
+    },
+    MapInfo {
+        // Length: Invalid|64MB
+        name: "scp_qspi_ahb",
+        description: "SCP QSPI AHB",
+        start: Some(0x0000_3000_0000),
+        end: Some(0x0000_3400_0000),
+        length: None,
+    },
+    MapInfo {
+        // Length: Invalid|4KB
+        name: "scp_qspi_apb",
+        description: "SCP QSPI APB",
+        start: Some(0x0000_3400_0000),
+        end: Some(0x0000_3400_1000),
+        length: None,
+    },
+    MapInfo {
+        // Length: Invalid|191MB
+        name: "reserved_part_of_scp_soc_expansion_memory_4",
+        description: "Reserved part of SCP SoC expansion memory 4",
+        start: Some(0x0000_3400_1000),
+        end: Some(0x0000_3ffe_0000),
+        length: None,
+    },
+    MapInfo {
+        name: "scp_pvt_ctrl",
+        description: "SCP PVT CTRL",
+        start: Some(0x0000_3ffe_0000),
+        end: Some(0x0000_3fff_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_scp_soc_expansion_memory_5",
+        description: "Reserved part of SCP SoC expansion memory 5",
+        start: Some(0x0000_3fff_0000),
+        end: Some(0x0000_3fff_a000),
+        length: Some(size!(40, KB)),
+    },
+    MapInfo {
+        name: "reserved_1",
+        description: "Reserved 1",
+        start: Some(0x0000_3fff_a000),
+        end: Some(0x0000_3fff_b000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "scp_i2c1_pmic",
+        description: "SCP I2C1 (PMIC)",
+        start: Some(0x0000_3fff_b000),
+        end: Some(0x0000_3fff_c000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "scp_i2c2_spd_pcc",
+        description: "SCP I2C2 (SPD-PCC)",
+        start: Some(0x0000_3fff_c000),
+        end: Some(0x0000_3fff_d000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "reserved_part_of_scp_soc_expansion_memory_6",
+        description: "Reserved part of SCP SoC expansion memory 6",
+        start: Some(0x0000_3fff_d000),
+        end: Some(0x0000_3fff_f000),
+        length: Some(size!(8, KB)),
+    },
+    MapInfo {
+        name: "scc_registers",
+        description: "SCC registers",
+        start: Some(0x0000_3fff_f000),
+        end: Some(0x0000_4000_0000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "scp_soc_expansion",
+        description: "SCP SoC expansion",
+        start: Some(0x0000_4000_0000),
+        end: Some(0x0000_4400_0000),
+        length: Some(size!(64, MB)),
+    },
+    MapInfo {
+        name: "scp_peripherals",
+        description: "SCP peripherals",
+        start: Some(0x0000_4400_0000),
+        end: Some(0x0000_4600_0000),
+        length: Some(size!(32, MB)),
+    },
+    MapInfo {
+        name: "mcp_soc_expansion",
+        description: "MCP SoC expansion",
+        start: Some(0x0000_4800_0000),
+        end: Some(0x0000_4c00_0000),
+        length: Some(size!(64, MB)),
+    },
+    MapInfo {
+        name: "memory_element_0_configuration",
+        description: "Memory element 0 configuration",
+        start: Some(0x0000_4e00_0000),
+        end: Some(0x0000_4e01_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "memory_element_0_manager",
+        description: "Memory element 0 manager",
+        start: Some(0x0000_4e01_0000),
+        end: Some(0x0000_4e02_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "memory_element_0_sensor_group",
+        description: "Memory element 0 sensor group",
+        start: Some(0x0000_4e02_0000),
+        end: Some(0x0000_4e03_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "memory_element_1_configuration",
+        description: "Memory element 1 configuration",
+        start: Some(0x0000_4e10_0000),
+        end: Some(0x0000_4e11_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "memory_element_1_manager",
+        description: "Memory element 1 manager",
+        start: Some(0x0000_4e11_0000),
+        end: Some(0x0000_4e12_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "memory_element_1_sensor_group",
+        description: "Memory element 1 sensor group",
+        start: Some(0x0000_4e12_0000),
+        end: Some(0x0000_4e13_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "element_management_peripherals",
+        description: "Element management peripherals",
+        start: Some(0x0000_5000_0000),
+        end: Some(0x0000_5080_0000),
+        length: Some(size!(8, MB)),
+    },
+    MapInfo {
+        name: "system_access_port_translated_to_0x0_4000_0000_to_0x0_7fff_ffff_of_ap_memory_map",
+        description: "System Access Port. Translated to 0x0_4000_0000 to 0x0_7FFF_FFFF of AP memory map.",
+        start: Some(0x0000_6000_0000),
+        end: Some(0x0000_a000_0000),
+        length: Some(size!(1, GB)),
+    },
+    MapInfo {
+        name: "system_access_port_translated_to_0x0_0000_0000_to_0x0_3fff_ffff_of_ap_memory_map_with_debug_address_translation_not_enabled_translated_to_0x4_0000_0000_to_0x4_3fff_ffff_of_ap_memory_map_with_debug_address_translation_enabled",
+        description: "System Access Port. Translated to 0x0_0000_0000 to 0x0_3FFF_FFFF of AP memory map with debug address translation not enabled. Translated to 0x4_0000_0000 to 0x4_3FFF_FFFF of AP memory map with debug address translation enabled.",
+        start: Some(0x0000_a000_0000),
+        end: Some(0x0000_e000_0000),
+        length: Some(size!(1, GB)),
+    },
+    MapInfo {
+        name: "private_peripheral_bus___internal",
+        description: "Private peripheral bus - Internal.",
+        start: Some(0x0000_e000_0000),
+        end: Some(0x0000_e004_0000),
+        length: Some(size!(256, KB)),
+    },
+    MapInfo {
+        name: "private_peripheral_bus___external",
+        description: "Private peripheral bus - External.",
+        start: Some(0x0000_e004_0000),
+        end: Some(0x0000_e010_0000),
+        length: Some(size!(768, KB)),
+    },
+    MapInfo {
+        name: "reserved_2",
+        description: "Reserved 2",
+        start: Some(0x0000_e010_0000),
+        end: Some(0x0001_0000_0000),
+        length: Some(size!(511, MB)),
+    },
+];
diff --git a/src/specs/morello/sdp_scp_sub_peripherals_map.rs b/src/specs/morello/sdp_scp_sub_peripherals_map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0cec73843465907bceb7fdd13fc1c2d7acb291ac
--- /dev/null
+++ b/src/specs/morello/sdp_scp_sub_peripherals_map.rs
@@ -0,0 +1,102 @@
+use crate::specs::common::*;
+
+pub static MAPINFOS: &'static [MapInfo] = &[
+    MapInfo {
+        name: "refclk_cntctl",
+        description: "REFCLK CNTCTL",
+        start: Some(0x0000_4400_0000),
+        end: Some(0x0000_4400_1000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "refclk_cntbase0",
+        description: "REFCLK CNTBase0",
+        start: Some(0x0000_4400_1000),
+        end: Some(0x0000_4400_2000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "scpuart",
+        description: "SCPUART",
+        start: Some(0x0000_4400_2000),
+        end: Some(0x0000_4400_3000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "watchdog_sp805",
+        description: "Watchdog (SP805)",
+        start: Some(0x0000_4400_6000),
+        end: Some(0x0000_4400_7000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "cs_cntcontrol",
+        description: "CS CNTCONTROL",
+        start: Some(0x0000_4400_a000),
+        end: Some(0x0000_4400_b000),
+        length: Some(size!(4, KB)),
+    },
+    MapInfo {
+        name: "refclk_general_timer_control",
+        description: "REFCLK general timer control",
+        start: Some(0x0000_4410_0000),
+        end: Some(0x0000_4411_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "cluster_0_time_frame",
+        description: "Cluster 0 time frame",
+        start: Some(0x0000_4411_0000),
+        end: Some(0x0000_4412_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "cluster_1_time_frame",
+        description: "Cluster 1 time frame",
+        start: Some(0x0000_4412_0000),
+        end: Some(0x0000_4413_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "ap2scp_message_handling_unit_mhu",
+        description: "AP2SCP Message Handling Unit (MHU)",
+        start: Some(0x0000_4500_0000),
+        end: Some(0x0000_4502_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "ap2scp_mhu_non_secure_ram",
+        description: "AP2SCP MHU Non-secure RAM",
+        start: Some(0x0000_452c_0000),
+        end: Some(0x0000_452e_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "ap2scp_mhu_secure_ram",
+        description: "AP2SCP MHU Secure RAM",
+        start: Some(0x0000_4540_0000),
+        end: Some(0x0000_4542_0000),
+        length: Some(size!(128, KB)),
+    },
+    MapInfo {
+        name: "scp2mch_mhu",
+        description: "SCP2MCH MHU",
+        start: Some(0x0000_4560_0000),
+        end: Some(0x0000_4561_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp2mcp_mhu_non_secure_ram",
+        description: "SCP2MCP MHU Non-secure RAM",
+        start: Some(0x0000_4561_0000),
+        end: Some(0x0000_4562_0000),
+        length: Some(size!(64, KB)),
+    },
+    MapInfo {
+        name: "scp2mcp_mhu_secure_ram",
+        description: "SCP2MCP MHU Secure RAM",
+        start: Some(0x0000_4562_0000),
+        end: Some(0x0000_4563_0000),
+        length: Some(size!(64, KB)),
+    },
+];