diff --git a/CHANGELOG-rust.md b/CHANGELOG-rust.md
index 45c55fca..7d3fc52c 100644
--- a/CHANGELOG-rust.md
+++ b/CHANGELOG-rust.md
@@ -5,6 +5,8 @@ This changelog tracks the Rust `svdtools` project. See
 
 ## [Unreleased]
 
+* Add `_env` string variable map
+
 ## [v0.3.10] 2024-02-26
 
 * Allow to specify `name` for `enumeratedValues`
diff --git a/src/patch/device.rs b/src/patch/device.rs
index c06da2a1..dc812f08 100644
--- a/src/patch/device.rs
+++ b/src/patch/device.rs
@@ -9,7 +9,7 @@ use std::{fs::File, io::Read, path::Path};
 use super::iterators::{MatchIter, Matched};
 use super::peripheral::{PeripheralExt, RegisterBlockExt};
 use super::yaml_ext::{AsType, GetVal};
-use super::{abspath, matchname, Config, PatchResult, Spec, VAL_LVL};
+use super::{abspath, matchname, update_env, Config, Env, PatchResult, Spec, VAL_LVL};
 use super::{make_address_block, make_address_blocks, make_cpu, make_interrupt, make_peripheral};
 use super::{make_dim_element, modify_dim_element, modify_register_properties};
 
@@ -33,14 +33,14 @@ pub trait DeviceExt {
     fn modify_cpu(&mut self, cmod: &Hash) -> PatchResult;
 
     /// Modify pspec inside device according to pmod
-    fn modify_peripheral(&mut self, pspec: &str, pmod: &Hash) -> PatchResult;
+    fn modify_peripheral(&mut self, pspec: &str, pmod: &Hash, env: &Env) -> PatchResult;
 
     /// Add pname given by padd to device
-    fn add_peripheral(&mut self, pname: &str, padd: &Hash) -> PatchResult;
+    fn add_peripheral(&mut self, pname: &str, padd: &Hash, env: &Env) -> PatchResult;
 
     /// Remove registers from pname and mark it as derivedFrom pderive.
     /// Update all derivedFrom referencing pname
-    fn derive_peripheral(&mut self, pname: &str, pderive: &Yaml) -> PatchResult;
+    fn derive_peripheral(&mut self, pname: &str, pderive: &Yaml, env: &Env) -> PatchResult;
 
     /// Move registers from pold to pnew.
     /// Update all derivedFrom referencing pold
@@ -55,6 +55,7 @@ pub trait DeviceExt {
         pspec: &str,
         peripheral: &Hash,
         config: &Config,
+        env: &Env,
     ) -> PatchResult;
 }
 
@@ -64,6 +65,9 @@ impl DeviceExt for Device {
     }
 
     fn process(&mut self, device: &Hash, config: &Config) -> PatchResult {
+        let mut env = Env::new();
+        update_env(&mut env, device)?;
+
         // Handle any deletions
         for pspec in device.str_vec_iter("_delete")? {
             self.delete_peripheral(pspec)
@@ -91,7 +95,7 @@ impl DeviceExt for Device {
                 "_peripherals" => {
                     for (pspec, pmod) in val.hash()? {
                         let pspec = pspec.str()?;
-                        self.modify_peripheral(pspec, pmod.hash()?)
+                        self.modify_peripheral(pspec, pmod.hash()?, &env)
                             .with_context(|| {
                                 format!("Modifying peripherals matched to `{pspec}`")
                             })?;
@@ -115,7 +119,7 @@ impl DeviceExt for Device {
                 }
 
                 _ => self
-                    .modify_peripheral(key, val.hash()?)
+                    .modify_peripheral(key, val.hash()?, &env)
                     .with_context(|| format!("Modifying peripherals matched to `{key}`"))?,
             }
         }
@@ -130,14 +134,14 @@ impl DeviceExt for Device {
         // Handle any new peripherals (!)
         for (pname, padd) in device.hash_iter("_add") {
             let pname = pname.str()?;
-            self.add_peripheral(pname, padd.hash()?)
+            self.add_peripheral(pname, padd.hash()?, &env)
                 .with_context(|| format!("Adding peripheral `{pname}`"))?;
         }
 
         // Handle any derived peripherals
         for (pname, pderive) in device.hash_iter("_derive") {
             let pname = pname.str()?;
-            self.derive_peripheral(pname, pderive)
+            self.derive_peripheral(pname, pderive, &env)
                 .with_context(|| format!("Deriving peripheral `{pname}` from `{pderive:?}`"))?;
         }
 
@@ -154,7 +158,7 @@ impl DeviceExt for Device {
             let periphspec = periphspec.str()?;
             if !periphspec.starts_with('_') {
                 //val["_path"] = device["_path"]; // TODO: check
-                self.process_peripheral(periphspec, val.hash()?, config)
+                self.process_peripheral(periphspec, val.hash()?, config, &env)
                     .with_context(|| format!("According to `{periphspec}`"))?;
             }
         }
@@ -218,11 +222,11 @@ impl DeviceExt for Device {
         Ok(())
     }
 
-    fn modify_peripheral(&mut self, pspec: &str, pmod: &Hash) -> PatchResult {
+    fn modify_peripheral(&mut self, pspec: &str, pmod: &Hash, env: &Env) -> PatchResult {
         let mut modified = HashSet::new();
         let ptags = self.iter_peripherals(pspec).collect::<Vec<_>>();
         if !ptags.is_empty() {
-            let peripheral_builder = make_peripheral(pmod, true)?;
+            let peripheral_builder = make_peripheral(pmod, true, env)?;
             let dim = make_dim_element(pmod)?;
             for ptag in ptags {
                 modified.insert(ptag.name.clone());
@@ -267,12 +271,12 @@ impl DeviceExt for Device {
         Ok(())
     }
 
-    fn add_peripheral(&mut self, pname: &str, padd: &Hash) -> PatchResult {
+    fn add_peripheral(&mut self, pname: &str, padd: &Hash, env: &Env) -> PatchResult {
         if self.get_peripheral(pname).is_some() {
             return Err(anyhow!("device already has a peripheral {pname}"));
         }
 
-        let pnew = make_peripheral(padd, false)?
+        let pnew = make_peripheral(padd, false, env)?
             .name(pname.to_string())
             .build(VAL_LVL)?;
         let pnew = if let Some(dim) = make_dim_element(padd)? {
@@ -285,7 +289,7 @@ impl DeviceExt for Device {
         Ok(())
     }
 
-    fn derive_peripheral(&mut self, pname: &str, pderive: &Yaml) -> PatchResult {
+    fn derive_peripheral(&mut self, pname: &str, pderive: &Yaml, env: &Env) -> PatchResult {
         let (pderive, info) = if let Some(pderive) = pderive.as_str() {
             (
                 pderive,
@@ -300,7 +304,7 @@ impl DeviceExt for Device {
             })?;
             (
                 pderive,
-                make_peripheral(hash, true)?.derived_from(Some(pderive.into())),
+                make_peripheral(hash, true, env)?.derived_from(Some(pderive.into())),
             )
         } else {
             return Err(anyhow!("derive: incorrect syntax for {pname}"));
@@ -415,13 +419,14 @@ impl DeviceExt for Device {
         pspec: &str,
         peripheral: &Hash,
         config: &Config,
+        env: &Env,
     ) -> PatchResult {
         // Find all peripherals that match the spec
         let mut pcount = 0;
         let (pspec, ignore) = pspec.spec();
         for ptag in self.iter_peripherals(pspec) {
             pcount += 1;
-            ptag.process(peripheral, config)
+            ptag.process(peripheral, config, env.clone())
                 .with_context(|| format!("Processing peripheral `{}`", ptag.name))?;
         }
         if !ignore && pcount == 0 {
diff --git a/src/patch/mod.rs b/src/patch/mod.rs
index c8535947..ff0bf6d0 100644
--- a/src/patch/mod.rs
+++ b/src/patch/mod.rs
@@ -1,5 +1,7 @@
 pub mod patch_cli;
 
+use std::borrow::Cow;
+use std::collections::HashMap;
 use std::fs::File;
 use std::io::{Read, Write};
 use std::path::{Path, PathBuf};
@@ -30,6 +32,31 @@ use crate::get_encoder_config;
 
 const VAL_LVL: ValidateLevel = ValidateLevel::Weak;
 
+pub type Env = HashMap<Cow<'static, str>, String>;
+
+fn update_env(env: &mut Env, dict: &Hash) -> PatchResult {
+    for (key, val) in dict.hash_iter("_env") {
+        let key = key.str()?;
+        let val = val.str()?;
+        env.insert(key.to_string().into(), val.to_string());
+    }
+    Ok(())
+}
+
+fn insert_env<'a>(s: &'a str, env: &Env) -> Cow<'a, str> {
+    let mut s = Cow::Borrowed(s);
+    for (k, v) in env {
+        let k = format!("`{k}`");
+        if s.contains(&k) {
+            s = s.replace(&k, v).into();
+        }
+    }
+    s
+}
+fn insert_env_opt(s: Option<&str>, env: &Env) -> Option<String> {
+    s.map(|s| insert_env(s, env).into_owned())
+}
+
 #[non_exhaustive]
 #[derive(Clone, Debug)]
 pub struct Config {
@@ -429,10 +456,10 @@ fn modify_dim_element<T: Clone>(
     Ok(())
 }
 
-fn make_field(fadd: &Hash) -> Result<FieldInfoBuilder> {
+fn make_field(fadd: &Hash, env: &Env) -> Result<FieldInfoBuilder> {
     let mut fnew = FieldInfo::builder()
-        .description(fadd.get_string("description")?)
-        .derived_from(fadd.get_string("derivedFrom")?)
+        .description(insert_env_opt(fadd.get_str("description")?, env))
+        .derived_from(insert_env_opt(fadd.get_str("derivedFrom")?, env))
         .access(fadd.get_str("access")?.and_then(Access::parse_str))
         .modified_write_values(
             fadd.get_str("modifiedWriteValues")?
@@ -460,11 +487,11 @@ fn make_field(fadd: &Hash) -> Result<FieldInfoBuilder> {
     Ok(fnew)
 }
 
-fn make_register(radd: &Hash) -> Result<RegisterInfoBuilder> {
+fn make_register(radd: &Hash, env: &Env) -> Result<RegisterInfoBuilder> {
     let mut rnew = RegisterInfo::builder()
         .display_name(radd.get_string("displayName")?)
-        .description(radd.get_string("description")?)
-        .derived_from(radd.get_string("derivedFrom")?)
+        .description(insert_env_opt(radd.get_str("description")?, env))
+        .derived_from(insert_env_opt(radd.get_str("derivedFrom")?, env))
         .alternate_group(radd.get_string("alternateGroup")?)
         .alternate_register(radd.get_string("alternateRegister")?)
         .properties(get_register_properties(radd)?)
@@ -473,7 +500,7 @@ fn make_register(radd: &Hash) -> Result<RegisterInfoBuilder> {
                 let mut fields = Vec::new();
                 for (fname, val) in h {
                     fields.push(
-                        make_field(val.hash()?)?
+                        make_field(val.hash()?, env)?
                             .name(fname.str()?.into())
                             .build(VAL_LVL)?
                             .single(),
@@ -519,10 +546,10 @@ fn make_register(radd: &Hash) -> Result<RegisterInfoBuilder> {
     Ok(rnew)
 }
 
-fn make_cluster(cadd: &Hash) -> Result<ClusterInfoBuilder> {
+fn make_cluster(cadd: &Hash, env: &Env) -> Result<ClusterInfoBuilder> {
     let mut cnew = ClusterInfo::builder()
-        .description(cadd.get_string("description")?)
-        .derived_from(cadd.get_string("derivedFrom")?)
+        .description(insert_env_opt(cadd.get_str("description")?, env))
+        .derived_from(insert_env_opt(cadd.get_str("derivedFrom")?, env))
         .default_register_properties(get_register_properties(cadd)?)
         .children(match cadd.get_hash("registers")? {
             Some(h) => {
@@ -530,7 +557,7 @@ fn make_cluster(cadd: &Hash) -> Result<ClusterInfoBuilder> {
                 for (rname, val) in h {
                     ch.push(RegisterCluster::Register({
                         let radd = val.hash()?;
-                        let reg = make_register(radd)?
+                        let reg = make_register(radd, env)?
                             .name(rname.str()?.into())
                             .build(VAL_LVL)?;
                         if let Some(dim) = make_dim_element(radd)? {
@@ -565,12 +592,12 @@ fn make_interrupt(iadd: &Hash) -> Result<InterruptBuilder> {
     Ok(int)
 }
 
-fn make_peripheral(padd: &Hash, modify: bool) -> Result<PeripheralInfoBuilder> {
+fn make_peripheral(padd: &Hash, modify: bool, env: &Env) -> Result<PeripheralInfoBuilder> {
     let mut pnew = PeripheralInfo::builder()
         .display_name(padd.get_string("displayName")?)
         .version(padd.get_string("version")?)
-        .description(padd.get_string("description")?)
-        .derived_from(padd.get_string("derivedFrom")?)
+        .description(insert_env_opt(padd.get_str("description")?, env))
+        .derived_from(insert_env_opt(padd.get_str("derivedFrom")?, env))
         .group_name(padd.get_string("groupName")?)
         .interrupt(if !modify {
             match padd.get_hash("interrupts")? {
@@ -619,7 +646,7 @@ fn make_peripheral(padd: &Hash, modify: bool) -> Result<PeripheralInfoBuilder> {
                     for (rname, val) in h.iter() {
                         regs.push(RegisterCluster::Register({
                             let radd = val.hash()?;
-                            let reg = make_register(radd)?
+                            let reg = make_register(radd, env)?
                                 .name(rname.str()?.into())
                                 .build(VAL_LVL)?;
                             if let Some(dim) = make_dim_element(radd)? {
diff --git a/src/patch/peripheral.rs b/src/patch/peripheral.rs
index 149f95e1..9c938d9a 100644
--- a/src/patch/peripheral.rs
+++ b/src/patch/peripheral.rs
@@ -11,7 +11,7 @@ use super::register::{RegisterExt, RegisterInfoExt};
 use super::yaml_ext::{AsType, GetVal, ToYaml};
 use super::{
     check_offsets, common_description, make_dim_element, matchname, matchsubspec,
-    modify_dim_element, spec_ind, Config, PatchResult, Spec, VAL_LVL,
+    modify_dim_element, spec_ind, update_env, Config, Env, PatchResult, Spec, VAL_LVL,
 };
 use super::{make_cluster, make_interrupt, make_register};
 
@@ -22,13 +22,13 @@ pub type RegMatchIterMut<'a, 'b> = MatchIter<'b, RegisterIterMut<'a>>;
 /// Collecting methods for processing peripheral contents
 pub trait PeripheralExt: InterruptExt + RegisterBlockExt {
     /// Work through a peripheral, handling all registers
-    fn process(&mut self, peripheral: &Hash, config: &Config) -> PatchResult;
+    fn process(&mut self, peripheral: &Hash, config: &Config, env: Env) -> PatchResult;
 }
 
 /// Collecting methods for processing cluster contents
 pub trait ClusterExt: RegisterBlockExt {
     /// Work through a cluster, handling all registers
-    fn process(&mut self, peripheral: &Hash, pname: &str, config: &Config) -> PatchResult;
+    fn process(&mut self, peripheral: &Hash, config: &Config, env: Env) -> PatchResult;
 }
 
 /// Collecting methods for processing peripheral interrupt contents
@@ -64,35 +64,47 @@ pub trait RegisterBlockExt {
     fn delete_cluster(&mut self, cspec: &str) -> PatchResult;
 
     /// Add rname given by radd to ptag
-    fn add_register(&mut self, rname: &str, radd: &Hash) -> PatchResult;
+    fn add_register(&mut self, rname: &str, radd: &Hash, env: &Env) -> PatchResult;
 
     /// Add cname given by cadd to ptag
-    fn add_cluster(&mut self, cname: &str, cadd: &Hash) -> PatchResult;
+    fn add_cluster(&mut self, cname: &str, cadd: &Hash, env: &Env) -> PatchResult;
 
     /// Remove fields from rname and mark it as derivedFrom rderive.
     /// Update all derivedFrom referencing rname
-    fn derive_register(&mut self, rname: &str, rderive: &Yaml) -> PatchResult;
+    fn derive_register(&mut self, rname: &str, rderive: &Yaml, env: &Env) -> PatchResult;
 
     /// Remove fields from rname and mark it as derivedFrom rderive.
     /// Update all derivedFrom referencing rname
     fn derive_cluster(&mut self, cname: &str, cderive: &Yaml) -> PatchResult;
 
     /// Add rname given by deriving from rcopy to ptag
-    fn copy_register(&mut self, rname: &str, rcopy: &Hash) -> PatchResult;
+    fn copy_register(&mut self, rname: &str, rcopy: &Hash, env: &Env) -> PatchResult;
 
     /// Add cname given by deriving from ccopy to ptag
     fn copy_cluster(&mut self, rname: &str, ccopy: &Hash) -> PatchResult;
 
     /// Modify rspec inside ptag according to rmod
-    fn modify_register(&mut self, rspec: &str, rmod: &Hash) -> PatchResult;
+    fn modify_register(&mut self, rspec: &str, rmod: &Hash, env: &Env) -> PatchResult;
 
     /// Modify cspec inside ptag according to cmod
-    fn modify_cluster(&mut self, cspec: &str, cmod: &Hash) -> PatchResult;
+    fn modify_cluster(&mut self, cspec: &str, cmod: &Hash, env: &Env) -> PatchResult;
     /// Work through a register, handling all fields
-    fn process_register(&mut self, rspec: &str, register: &Hash, config: &Config) -> PatchResult;
+    fn process_register(
+        &mut self,
+        rspec: &str,
+        register: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult;
 
     /// Work through a cluster, handling all contents
-    fn process_cluster(&mut self, cspec: &str, cluster: &Hash, config: &Config) -> PatchResult;
+    fn process_cluster(
+        &mut self,
+        cspec: &str,
+        cluster: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult;
 
     /// Delete substring from the beginning of register names inside ptag
     fn strip_start(&mut self, prefix: &str) -> PatchResult;
@@ -101,17 +113,33 @@ pub trait RegisterBlockExt {
     fn strip_end(&mut self, suffix: &str) -> PatchResult;
 
     /// Collect same registers in peripheral into register array
-    fn collect_in_array(&mut self, rspec: &str, rmod: &Hash, config: &Config) -> PatchResult;
+    fn collect_in_array(
+        &mut self,
+        rspec: &str,
+        rmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult;
 
     /// Collect registers in peripheral into clusters
-    fn collect_in_cluster(&mut self, cname: &str, cmod: &Hash, config: &Config) -> PatchResult;
+    fn collect_in_cluster(
+        &mut self,
+        cname: &str,
+        cmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult;
 
     /// Clear contents of all fields inside registers matched by rspec
     fn clear_fields(&mut self, rspec: &str) -> PatchResult;
 }
 
 impl PeripheralExt for Peripheral {
-    fn process(&mut self, pmod: &Hash, config: &Config) -> PatchResult {
+    fn process(&mut self, pmod: &Hash, config: &Config, mut env: Env) -> PatchResult {
+        env.insert("peripheral".into(), self.name.clone());
+        env.insert("block_path".into(), self.name.clone());
+        update_env(&mut env, pmod)?;
+
         // For derived peripherals, only process interrupts
         if self.derived_from.is_some() {
             if let Some(deletions) = pmod.get_hash("_delete").ok().flatten() {
@@ -200,7 +228,7 @@ impl PeripheralExt for Peripheral {
                     for (rname, val) in rcopy.hash()? {
                         let rname = rname.str()?;
                         let rcopy = val.hash()?;
-                        self.copy_register(rname, rcopy).with_context(|| {
+                        self.copy_register(rname, rcopy, &env).with_context(|| {
                             format!("Copying register `{rname}` from `{val:?}`")
                         })?;
                     }
@@ -215,7 +243,7 @@ impl PeripheralExt for Peripheral {
                 }
                 _ => {
                     let rcopy = rcopy.hash()?;
-                    self.copy_register(rname, rcopy)
+                    self.copy_register(rname, rcopy, &env)
                         .with_context(|| format!("Copying register `{rname}` from `{rcopy:?}`"))?;
                 }
             }
@@ -238,7 +266,7 @@ impl PeripheralExt for Peripheral {
                 "_registers" => {
                     for (rspec, val) in rmod {
                         let rspec = rspec.str()?;
-                        self.modify_register(rspec, val.hash()?)
+                        self.modify_register(rspec, val.hash()?, &env)
                             .with_context(|| format!("Modifying registers matched to `{rspec}`"))?;
                     }
                 }
@@ -253,12 +281,12 @@ impl PeripheralExt for Peripheral {
                 "_cluster" => {
                     for (cspec, val) in rmod {
                         let cspec = cspec.str()?;
-                        self.modify_cluster(cspec, val.hash()?)
+                        self.modify_cluster(cspec, val.hash()?, &env)
                             .with_context(|| format!("Modifying clusters matched to `{cspec}`"))?;
                     }
                 }
                 rspec => self
-                    .modify_register(rspec, rmod)
+                    .modify_register(rspec, rmod, &env)
                     .with_context(|| format!("Modifying registers matched to `{rspec}`"))?,
             }
         }
@@ -277,14 +305,14 @@ impl PeripheralExt for Peripheral {
                 "_registers" => {
                     for (rname, val) in radd {
                         let rname = rname.str()?;
-                        self.add_register(rname, val.hash()?)
+                        self.add_register(rname, val.hash()?, &env)
                             .with_context(|| format!("Adding register `{rname}`"))?;
                     }
                 }
                 "_clusters" => {
                     for (cname, val) in radd {
                         let cname = cname.str()?;
-                        self.add_cluster(cname, val.hash()?)
+                        self.add_cluster(cname, val.hash()?, &env)
                             .with_context(|| format!("Adding cluster `{cname}`"))?;
                     }
                 }
@@ -296,7 +324,7 @@ impl PeripheralExt for Peripheral {
                     }
                 }
                 rname => self
-                    .add_register(rname, radd)
+                    .add_register(rname, radd, &env)
                     .with_context(|| format!("Adding register `{rname}`"))?,
             }
         }
@@ -307,7 +335,7 @@ impl PeripheralExt for Peripheral {
                 "_registers" => {
                     for (rname, val) in rderive.hash()? {
                         let rname = rname.str()?;
-                        self.derive_register(rname, val).with_context(|| {
+                        self.derive_register(rname, val, &env).with_context(|| {
                             format!("Deriving register `{rname}` from `{val:?}`")
                         })?;
                     }
@@ -321,9 +349,10 @@ impl PeripheralExt for Peripheral {
                     }
                 }
                 _ => {
-                    self.derive_register(rname, rderive).with_context(|| {
-                        format!("Deriving register `{rname}` from `{rderive:?}`")
-                    })?;
+                    self.derive_register(rname, rderive, &env)
+                        .with_context(|| {
+                            format!("Deriving register `{rname}` from `{rderive:?}`")
+                        })?;
                 }
             }
         }
@@ -332,7 +361,7 @@ impl PeripheralExt for Peripheral {
         for (rspec, register) in pmod {
             let rspec = rspec.str()?;
             if !rspec.starts_with('_') {
-                self.process_register(rspec, register.hash()?, config)
+                self.process_register(rspec, register.hash()?, config, &env)
                     .with_context(|| format!("According to `{rspec}`"))?;
             }
         }
@@ -340,14 +369,14 @@ impl PeripheralExt for Peripheral {
         // Collect registers in arrays
         for (rspec, rmod) in pmod.hash_iter("_array") {
             let rspec = rspec.str()?;
-            self.collect_in_array(rspec, rmod.hash()?, config)
+            self.collect_in_array(rspec, rmod.hash()?, config, &env)
                 .with_context(|| format!("Collecting registers matched to `{rspec}` in array"))?;
         }
 
         // Collect registers in clusters
         for (cname, cmod) in pmod.hash_iter("_cluster") {
             let cname = cname.str()?;
-            self.collect_in_cluster(cname, cmod.hash()?, config)
+            self.collect_in_cluster(cname, cmod.hash()?, config, &env)
                 .with_context(|| format!("Collecting registers in cluster `{cname}`"))?;
         }
 
@@ -355,7 +384,7 @@ impl PeripheralExt for Peripheral {
         for (cspec, cluster) in pmod.hash_iter("_clusters") {
             let cspec = cspec.str()?;
             if !cspec.starts_with('_') {
-                self.process_cluster(cspec, cluster.hash()?, config)
+                self.process_cluster(cspec, cluster.hash()?, config, &env)
                     .with_context(|| format!("According to `{cspec}`"))?;
             }
         }
@@ -406,11 +435,11 @@ impl RegisterBlockExt for Peripheral {
         self.clusters_mut().matched(spec)
     }
 
-    fn modify_register(&mut self, rspec: &str, rmod: &Hash) -> PatchResult {
+    fn modify_register(&mut self, rspec: &str, rmod: &Hash, env: &Env) -> PatchResult {
         // TODO: empty error
         let rtags = self.iter_registers(rspec).collect::<Vec<_>>();
         if !rtags.is_empty() {
-            let register_builder = make_register(rmod)?;
+            let register_builder = make_register(rmod, env)?;
             let dim = make_dim_element(rmod)?;
             for rtag in rtags {
                 modify_dim_element(rtag, &dim)?;
@@ -423,7 +452,7 @@ impl RegisterBlockExt for Peripheral {
         Ok(())
     }
 
-    fn add_register(&mut self, rname: &str, radd: &Hash) -> PatchResult {
+    fn add_register(&mut self, rname: &str, radd: &Hash, env: &Env) -> PatchResult {
         if self.registers().any(|r| r.name == rname) {
             return Err(anyhow!(
                 "peripheral {} already has a register {rname}",
@@ -433,7 +462,9 @@ impl RegisterBlockExt for Peripheral {
         self.registers
             .get_or_insert_with(Default::default)
             .push(RegisterCluster::Register({
-                let reg = make_register(radd)?.name(rname.into()).build(VAL_LVL)?;
+                let reg = make_register(radd, env)?
+                    .name(rname.into())
+                    .build(VAL_LVL)?;
                 if let Some(dim) = make_dim_element(radd)? {
                     reg.array(dim.build(VAL_LVL)?)
                 } else {
@@ -443,7 +474,7 @@ impl RegisterBlockExt for Peripheral {
         Ok(())
     }
 
-    fn add_cluster(&mut self, cname: &str, cadd: &Hash) -> PatchResult {
+    fn add_cluster(&mut self, cname: &str, cadd: &Hash, env: &Env) -> PatchResult {
         if self.clusters().any(|c| c.name == cname) {
             return Err(anyhow!(
                 "peripheral {} already has a cluster {cname}",
@@ -453,7 +484,7 @@ impl RegisterBlockExt for Peripheral {
         self.registers
             .get_or_insert_with(Default::default)
             .push(RegisterCluster::Cluster({
-                let cl = make_cluster(cadd)?.name(cname.into()).build(VAL_LVL)?;
+                let cl = make_cluster(cadd, env)?.name(cname.into()).build(VAL_LVL)?;
                 if let Some(dim) = make_dim_element(cadd)? {
                     cl.array(dim.build(VAL_LVL)?)
                 } else {
@@ -463,7 +494,7 @@ impl RegisterBlockExt for Peripheral {
         Ok(())
     }
 
-    fn derive_register(&mut self, rname: &str, rderive: &Yaml) -> PatchResult {
+    fn derive_register(&mut self, rname: &str, rderive: &Yaml, env: &Env) -> PatchResult {
         let (rderive, info) = if let Some(rderive) = rderive.as_str() {
             (
                 rderive,
@@ -475,7 +506,7 @@ impl RegisterBlockExt for Peripheral {
             })?;
             (
                 rderive,
-                make_register(hash)?.derived_from(Some(rderive.into())),
+                make_register(hash, env)?.derived_from(Some(rderive.into())),
             )
         } else {
             return Err(anyhow!("derive: incorrect syntax for {rname}"));
@@ -510,7 +541,7 @@ impl RegisterBlockExt for Peripheral {
         todo!()
     }
 
-    fn copy_register(&mut self, rname: &str, rcopy: &Hash) -> PatchResult {
+    fn copy_register(&mut self, rname: &str, rcopy: &Hash, env: &Env) -> PatchResult {
         let srcname = rcopy.get_str("_from")?.ok_or_else(|| {
             anyhow!("derive: source register not given, please add a _from field to {rname}")
         })?;
@@ -520,7 +551,7 @@ impl RegisterBlockExt for Peripheral {
             .find(|r| r.name == srcname)
             .ok_or_else(|| anyhow!("peripheral {} does not have register {srcname}", self.name))?
             .clone();
-        let fixes = make_register(rcopy)?
+        let fixes = make_register(rcopy, env)?
             .name(rname.into())
             .display_name(Some("".into()));
         // Modifying fields in derived register not implemented
@@ -557,11 +588,11 @@ impl RegisterBlockExt for Peripheral {
         Ok(())
     }
 
-    fn modify_cluster(&mut self, cspec: &str, cmod: &Hash) -> PatchResult {
+    fn modify_cluster(&mut self, cspec: &str, cmod: &Hash, env: &Env) -> PatchResult {
         // TODO: empty error
         let ctags = self.iter_clusters(cspec).collect::<Vec<_>>();
         if !ctags.is_empty() {
-            let cluster_builder = make_cluster(cmod)?;
+            let cluster_builder = make_cluster(cmod, env)?;
             let dim = make_dim_element(cmod)?;
             for ctag in ctags {
                 modify_dim_element(ctag, &dim)?;
@@ -618,18 +649,28 @@ impl RegisterBlockExt for Peripheral {
         Ok(())
     }
 
-    fn collect_in_array(&mut self, rspec: &str, rmod: &Hash, config: &Config) -> PatchResult {
-        let pname = self.name.clone();
+    fn collect_in_array(
+        &mut self,
+        rspec: &str,
+        rmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         if let Some(regs) = self.registers.as_mut() {
-            collect_in_array(regs, &pname, rspec, rmod, config)?;
+            collect_in_array(regs, rspec, rmod, config, env)?;
         }
         Ok(())
     }
 
-    fn collect_in_cluster(&mut self, cname: &str, cmod: &Hash, config: &Config) -> PatchResult {
-        let pname = self.name.clone();
+    fn collect_in_cluster(
+        &mut self,
+        cname: &str,
+        cmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         if let Some(regs) = self.registers.as_mut() {
-            collect_in_cluster(regs, &pname, cname, cmod, config)?;
+            collect_in_cluster(regs, cname, cmod, config, env)?;
         }
         Ok(())
     }
@@ -644,19 +685,25 @@ impl RegisterBlockExt for Peripheral {
         Ok(())
     }
 
-    fn process_register(&mut self, rspec: &str, rmod: &Hash, config: &Config) -> PatchResult {
+    fn process_register(
+        &mut self,
+        rspec: &str,
+        rmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         // Find all registers that match the spec
         let mut rcount = 0;
-        let pname = self.name.clone();
         let (rspec, ignore) = rspec.spec();
         for rtag in self.iter_registers(rspec) {
             rcount += 1;
-            rtag.process(rmod, &pname, config)
+            rtag.process(rmod, config, env.clone())
                 .with_context(|| format!("Processing register `{}`", rtag.name))?;
         }
         if !ignore && rcount == 0 {
             Err(anyhow!(
-                "Could not find `{pname}:{rspec}. Present registers: {}.`",
+                "Could not find `{}:{rspec}. Present registers: {}.`",
+                env.get("block_path").unwrap(),
                 self.registers().map(|r| r.name.as_str()).join(", ")
             ))
         } else {
@@ -664,19 +711,25 @@ impl RegisterBlockExt for Peripheral {
         }
     }
 
-    fn process_cluster(&mut self, cspec: &str, cmod: &Hash, config: &Config) -> PatchResult {
+    fn process_cluster(
+        &mut self,
+        cspec: &str,
+        cmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         // Find all clusters that match the spec
         let mut ccount = 0;
-        let pname = self.name.clone();
         let (cspec, ignore) = cspec.spec();
         for ctag in self.iter_clusters(cspec) {
             ccount += 1;
-            ctag.process(cmod, &pname, config)
+            ctag.process(cmod, config, env.clone())
                 .with_context(|| format!("Processing cluster `{}`", ctag.name))?;
         }
         if !ignore && ccount == 0 {
             Err(anyhow!(
-                "Could not find `{pname}:{cspec}. Present clusters: {}.`",
+                "Could not find `{}:{cspec}. Present clusters: {}.`",
+                env.get("block_path").unwrap(),
                 self.clusters().map(|c| c.name.as_str()).join(", ")
             ))
         } else {
@@ -686,7 +739,12 @@ impl RegisterBlockExt for Peripheral {
 }
 
 impl ClusterExt for Cluster {
-    fn process(&mut self, pmod: &Hash, _pname: &str, config: &Config) -> PatchResult {
+    fn process(&mut self, pmod: &Hash, config: &Config, mut env: Env) -> PatchResult {
+        env.insert("cluster".into(), self.name.clone());
+        env.entry("block_path".into())
+            .and_modify(|p| *p = format!("{p}.{}", self.name));
+        update_env(&mut env, pmod)?;
+
         // Handle deletions
         if let Some(deletions) = pmod.get(&"_delete".to_yaml()) {
             match deletions {
@@ -735,7 +793,7 @@ impl ClusterExt for Cluster {
                     for (rname, val) in rcopy.hash()? {
                         let rname = rname.str()?;
                         let rcopy = val.hash()?;
-                        self.copy_register(rname, rcopy).with_context(|| {
+                        self.copy_register(rname, rcopy, &env).with_context(|| {
                             format!("Copying register `{rname}` from `{val:?}`")
                         })?;
                     }
@@ -750,7 +808,7 @@ impl ClusterExt for Cluster {
                 }
                 _ => {
                     let rcopy = rcopy.hash()?;
-                    self.copy_register(rname, rcopy)
+                    self.copy_register(rname, rcopy, &env)
                         .with_context(|| format!("Copying register `{rname}` from `{rcopy:?}`"))?;
                 }
             }
@@ -773,19 +831,19 @@ impl ClusterExt for Cluster {
                 "_registers" => {
                     for (rspec, val) in rmod {
                         let rspec = rspec.str()?;
-                        self.modify_register(rspec, val.hash()?)
+                        self.modify_register(rspec, val.hash()?, &env)
                             .with_context(|| format!("Modifying registers matched to `{rspec}`"))?;
                     }
                 }
                 "_cluster" => {
                     for (cspec, val) in rmod {
                         let cspec = cspec.str()?;
-                        self.modify_cluster(cspec, val.hash()?)
+                        self.modify_cluster(cspec, val.hash()?, &env)
                             .with_context(|| format!("Modifying clusters matched to `{cspec}`"))?;
                     }
                 }
                 rspec => self
-                    .modify_register(rspec, rmod)
+                    .modify_register(rspec, rmod, &env)
                     .with_context(|| format!("Modifying registers matched to `{rspec}`"))?,
             }
         }
@@ -804,19 +862,19 @@ impl ClusterExt for Cluster {
                 "_registers" => {
                     for (rname, val) in radd {
                         let rname = rname.str()?;
-                        self.add_register(rname, val.hash()?)
+                        self.add_register(rname, val.hash()?, &env)
                             .with_context(|| format!("Adding register `{rname}`"))?;
                     }
                 }
                 "_clusters" => {
                     for (cname, val) in radd {
                         let cname = cname.str()?;
-                        self.add_cluster(cname, val.hash()?)
+                        self.add_cluster(cname, val.hash()?, &env)
                             .with_context(|| format!("Adding cluster `{cname}`"))?;
                     }
                 }
                 rname => self
-                    .add_register(rname, radd)
+                    .add_register(rname, radd, &env)
                     .with_context(|| format!("Adding register `{rname}`"))?,
             }
         }
@@ -827,7 +885,7 @@ impl ClusterExt for Cluster {
                 "_registers" => {
                     for (rname, val) in rderive.hash()? {
                         let rname = rname.str()?;
-                        self.derive_register(rname, val).with_context(|| {
+                        self.derive_register(rname, val, &env).with_context(|| {
                             format!("Deriving register `{rname}` from `{val:?}`")
                         })?;
                     }
@@ -841,9 +899,10 @@ impl ClusterExt for Cluster {
                     }
                 }
                 _ => {
-                    self.derive_register(rname, rderive).with_context(|| {
-                        format!("Deriving register `{rname}` from `{rderive:?}`")
-                    })?;
+                    self.derive_register(rname, rderive, &env)
+                        .with_context(|| {
+                            format!("Deriving register `{rname}` from `{rderive:?}`")
+                        })?;
                 }
             }
         }
@@ -852,7 +911,7 @@ impl ClusterExt for Cluster {
         for (cspec, cluster) in pmod.hash_iter("_clusters") {
             let cspec = cspec.str()?;
             if !cspec.starts_with('_') {
-                self.process_cluster(cspec, cluster.hash()?, config)
+                self.process_cluster(cspec, cluster.hash()?, config, &env)
                     .with_context(|| format!("According to `{cspec}`"))?;
             }
         }
@@ -861,7 +920,7 @@ impl ClusterExt for Cluster {
         for (rspec, register) in pmod {
             let rspec = rspec.str()?;
             if !rspec.starts_with('_') {
-                self.process_register(rspec, register.hash()?, config)
+                self.process_register(rspec, register.hash()?, config, &env)
                     .with_context(|| format!("According to `{rspec}`"))?;
             }
         }
@@ -869,14 +928,14 @@ impl ClusterExt for Cluster {
         // Collect registers in arrays
         for (rspec, rmod) in pmod.hash_iter("_array") {
             let rspec = rspec.str()?;
-            self.collect_in_array(rspec, rmod.hash()?, config)
+            self.collect_in_array(rspec, rmod.hash()?, config, &env)
                 .with_context(|| format!("Collecting registers matched to `{rspec}` in array"))?;
         }
 
         // Collect registers in clusters
         for (cname, cmod) in pmod.hash_iter("_cluster") {
             let cname = cname.str()?;
-            self.collect_in_cluster(cname, cmod.hash()?, config)
+            self.collect_in_cluster(cname, cmod.hash()?, config, &env)
                 .with_context(|| format!("Collecting registers in cluster `{cname}`"))?;
         }
 
@@ -893,10 +952,10 @@ impl RegisterBlockExt for Cluster {
         self.clusters_mut().matched(spec)
     }
 
-    fn modify_register(&mut self, rspec: &str, rmod: &Hash) -> PatchResult {
+    fn modify_register(&mut self, rspec: &str, rmod: &Hash, env: &Env) -> PatchResult {
         let rtags = self.iter_registers(rspec).collect::<Vec<_>>();
         if !rtags.is_empty() {
-            let register_builder = make_register(rmod)?;
+            let register_builder = make_register(rmod, env)?;
             let dim = make_dim_element(rmod)?;
             for rtag in rtags {
                 modify_dim_element(rtag, &dim)?;
@@ -909,7 +968,7 @@ impl RegisterBlockExt for Cluster {
         Ok(())
     }
 
-    fn add_register(&mut self, rname: &str, radd: &Hash) -> PatchResult {
+    fn add_register(&mut self, rname: &str, radd: &Hash, env: &Env) -> PatchResult {
         if self.registers().any(|r| r.name == rname) {
             return Err(anyhow!(
                 "peripheral {} already has a register {rname}",
@@ -917,7 +976,9 @@ impl RegisterBlockExt for Cluster {
             ));
         }
         self.children.push(RegisterCluster::Register({
-            let reg = make_register(radd)?.name(rname.into()).build(VAL_LVL)?;
+            let reg = make_register(radd, env)?
+                .name(rname.into())
+                .build(VAL_LVL)?;
             if let Some(dim) = make_dim_element(radd)? {
                 reg.array(dim.build(VAL_LVL)?)
             } else {
@@ -927,7 +988,7 @@ impl RegisterBlockExt for Cluster {
         Ok(())
     }
 
-    fn add_cluster(&mut self, cname: &str, cadd: &Hash) -> PatchResult {
+    fn add_cluster(&mut self, cname: &str, cadd: &Hash, env: &Env) -> PatchResult {
         if self.clusters().any(|c| c.name == cname) {
             return Err(anyhow!(
                 "peripheral {} already has a register {cname}",
@@ -935,7 +996,7 @@ impl RegisterBlockExt for Cluster {
             ));
         }
         self.children.push(RegisterCluster::Cluster({
-            let cl = make_cluster(cadd)?.name(cname.into()).build(VAL_LVL)?;
+            let cl = make_cluster(cadd, env)?.name(cname.into()).build(VAL_LVL)?;
             if let Some(dim) = make_dim_element(cadd)? {
                 cl.array(dim.build(VAL_LVL)?)
             } else {
@@ -945,7 +1006,7 @@ impl RegisterBlockExt for Cluster {
         Ok(())
     }
 
-    fn derive_register(&mut self, rname: &str, rderive: &Yaml) -> PatchResult {
+    fn derive_register(&mut self, rname: &str, rderive: &Yaml, env: &Env) -> PatchResult {
         let (rderive, info) = if let Some(rderive) = rderive.as_str() {
             (
                 rderive,
@@ -957,7 +1018,7 @@ impl RegisterBlockExt for Cluster {
             })?;
             (
                 rderive,
-                make_register(hash)?.derived_from(Some(rderive.into())),
+                make_register(hash, env)?.derived_from(Some(rderive.into())),
             )
         } else {
             return Err(anyhow!("derive: incorrect syntax for {rname}"));
@@ -990,7 +1051,7 @@ impl RegisterBlockExt for Cluster {
         todo!()
     }
 
-    fn copy_register(&mut self, rname: &str, rcopy: &Hash) -> PatchResult {
+    fn copy_register(&mut self, rname: &str, rcopy: &Hash, env: &Env) -> PatchResult {
         let srcname = rcopy.get_str("_from")?.ok_or_else(|| {
             anyhow!("derive: source register not given, please add a _from field to {rname}")
         })?;
@@ -1000,7 +1061,7 @@ impl RegisterBlockExt for Cluster {
             .find(|r| r.name == srcname)
             .ok_or_else(|| anyhow!("peripheral {} does not have register {srcname}", self.name,))?
             .clone();
-        let fixes = make_register(rcopy)?
+        let fixes = make_register(rcopy, env)?
             .name(rname.into())
             .display_name(Some("".into()));
         // Modifying fields in derived register not implemented
@@ -1030,10 +1091,10 @@ impl RegisterBlockExt for Cluster {
         Ok(())
     }
 
-    fn modify_cluster(&mut self, cspec: &str, cmod: &Hash) -> PatchResult {
+    fn modify_cluster(&mut self, cspec: &str, cmod: &Hash, env: &Env) -> PatchResult {
         let ctags = self.iter_clusters(cspec).collect::<Vec<_>>();
         if !ctags.is_empty() {
-            let cluster_builder = make_cluster(cmod)?;
+            let cluster_builder = make_cluster(cmod, env)?;
             let dim = make_dim_element(cmod)?;
             for ctag in ctags {
                 modify_dim_element(ctag, &dim)?;
@@ -1090,16 +1151,26 @@ impl RegisterBlockExt for Cluster {
         Ok(())
     }
 
-    fn collect_in_array(&mut self, rspec: &str, rmod: &Hash, config: &Config) -> PatchResult {
-        let pname = self.name.clone();
+    fn collect_in_array(
+        &mut self,
+        rspec: &str,
+        rmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         let regs = &mut self.children;
-        collect_in_array(regs, &pname, rspec, rmod, config)
+        collect_in_array(regs, rspec, rmod, config, env)
     }
 
-    fn collect_in_cluster(&mut self, cname: &str, cmod: &Hash, config: &Config) -> PatchResult {
-        let pname = self.name.clone();
+    fn collect_in_cluster(
+        &mut self,
+        cname: &str,
+        cmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         let regs = &mut self.children;
-        collect_in_cluster(regs, &pname, cname, cmod, config)
+        collect_in_cluster(regs, cname, cmod, config, env)
     }
 
     fn clear_fields(&mut self, rspec: &str) -> PatchResult {
@@ -1112,20 +1183,25 @@ impl RegisterBlockExt for Cluster {
         Ok(())
     }
 
-    fn process_register(&mut self, rspec: &str, rmod: &Hash, config: &Config) -> PatchResult {
+    fn process_register(
+        &mut self,
+        rspec: &str,
+        rmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         // Find all registers that match the spec
         let mut rcount = 0;
-        let pname = self.name.clone();
         let (rspec, ignore) = rspec.spec();
         for rtag in self.iter_registers(rspec) {
             rcount += 1;
-            rtag.process(rmod, &pname, config)
+            rtag.process(rmod, config, env.clone())
                 .with_context(|| format!("Processing register `{}`", rtag.name))?;
         }
         if !ignore && rcount == 0 {
             Err(anyhow!(
-                "Could not find `{pname}:{}:{rspec}. Present registers: {}.`",
-                self.name,
+                "Could not find `{}:{rspec}. Present registers: {}.`",
+                env.get("block_path").unwrap(),
                 self.registers().map(|r| r.name.as_str()).join(", ")
             ))
         } else {
@@ -1133,20 +1209,25 @@ impl RegisterBlockExt for Cluster {
         }
     }
 
-    fn process_cluster(&mut self, cspec: &str, cmod: &Hash, config: &Config) -> PatchResult {
+    fn process_cluster(
+        &mut self,
+        cspec: &str,
+        cmod: &Hash,
+        config: &Config,
+        env: &Env,
+    ) -> PatchResult {
         // Find all clusters that match the spec
         let mut ccount = 0;
-        let pname = self.name.clone();
         let (cspec, ignore) = cspec.spec();
         for ctag in self.iter_clusters(cspec) {
             ccount += 1;
-            ctag.process(cmod, &pname, config)
+            ctag.process(cmod, config, env.clone())
                 .with_context(|| format!("Processing cluster `{}`", ctag.name))?;
         }
         if !ignore && ccount == 0 {
             Err(anyhow!(
-                "Could not find `{pname}:{}:{cspec}. Present clusters: {}.`",
-                self.name,
+                "Could not find `{}:{cspec}. Present clusters: {}.`",
+                env.get("block_path").unwrap(),
                 self.clusters().map(|c| c.name.as_str()).join(", ")
             ))
         } else {
@@ -1157,10 +1238,10 @@ impl RegisterBlockExt for Cluster {
 
 fn collect_in_array(
     regs: &mut Vec<RegisterCluster>,
-    path: &str,
     rspec: &str,
     rmod: &Hash,
     config: &Config,
+    env: &Env,
 ) -> PatchResult {
     let mut registers = Vec::new();
     let mut place = usize::MAX;
@@ -1182,7 +1263,8 @@ fn collect_in_array(
             return Ok(());
         }
         return Err(anyhow!(
-            "{path}: registers {rspec} not found. Present registers: {}.`",
+            "{}: registers {rspec} not found. Present registers: {}.`",
+            env.get("block_path").unwrap(),
             regs.iter()
                 .filter_map(|rc| match rc {
                     RegisterCluster::Register(r) => Some(r.name.as_str()),
@@ -1222,7 +1304,7 @@ fn collect_in_array(
     if !check_offsets(&offsets, dim_increment) {
         return Err(anyhow!(
             "{}: registers cannot be collected into {rspec} array. Different addressOffset increments",
-            path
+            env.get("block_path").unwrap(),
         ));
     }
     let bitmasks = registers
@@ -1232,7 +1314,7 @@ fn collect_in_array(
     if !bitmasks.iter().all(|&m| m == bitmasks[0]) {
         return Err(anyhow!(
             "{}: registers cannot be collected into {rspec} array. Different bit masks",
-            path
+            env.get("block_path").unwrap(),
         ));
     }
 
@@ -1251,7 +1333,7 @@ fn collect_in_array(
         registers[0].description = common_description(&descs, &dim_index).ok_or_else(|| {
             anyhow!(
                 "{}: registers cannot be collected into {rspec} array. Please, specify description",
-                path
+                env.get("block_path").unwrap(),
             )
         })?;
     }
@@ -1267,7 +1349,7 @@ fn collect_in_array(
         registers[0].display_name = common_description(&names, &dim_index).ok_or_else(|| {
             anyhow!(
                 "{}: registers cannot be collected into {rspec} array. Please, specify displayName",
-                path
+                env.get("block_path").unwrap(),
             )
         })?;
     }
@@ -1281,7 +1363,7 @@ fn collect_in_array(
     );
     let mut config = config.clone();
     config.update_fields = true;
-    reg.process(rmod, path, &config)
+    reg.process(rmod, &config, env.clone())
         .with_context(|| format!("Processing register `{}`", reg.name))?;
     regs.insert(place, RegisterCluster::Register(reg));
     Ok(())
@@ -1289,10 +1371,10 @@ fn collect_in_array(
 
 fn collect_in_cluster(
     regs: &mut Vec<RegisterCluster>,
-    path: &str,
     cname: &str,
     cmod: &Hash,
     config: &Config,
+    env: &Env,
 ) -> PatchResult {
     let mut rdict = linked_hash_map::LinkedHashMap::new();
     let mut first = None;
@@ -1328,7 +1410,8 @@ fn collect_in_cluster(
                 continue;
             }
             return Err(anyhow!(
-                "{path}: registers {rspec} not found. Present registers: {}.`",
+                "{}: registers {rspec} not found. Present registers: {}.`",
+                env.get("block_path").unwrap(),
                 regs.iter()
                     .filter_map(|rc| match rc {
                         RegisterCluster::Register(r) => Some(r.name.as_str()),
@@ -1340,7 +1423,10 @@ fn collect_in_cluster(
         rspecs.push(rspec.to_string());
         if single {
             if registers.len() > 1 {
-                return Err(anyhow!("{path}: more than one registers {rspec} found"));
+                return Err(anyhow!(
+                    "{}: more than one registers {rspec} found",
+                    env.get("block_path").unwrap()
+                ));
             }
         } else {
             registers.sort_by_key(|r| r.address_offset);
@@ -1365,12 +1451,14 @@ fn collect_in_cluster(
                 let len = registers.len();
                 if dim != len {
                     return Err(anyhow!(
-                        "{path}: registers cannot be collected into {cname} cluster. Different number of registers {rspec} ({len}) and {rspec1} ({dim})"
+                        "{}: registers cannot be collected into {cname} cluster. Different number of registers {rspec} ({len}) and {rspec1} ({dim})",
+                        env.get("block_path").unwrap()
                     ));
                 }
                 if dim_index != new_dim_index {
                     return Err(anyhow!(
-                        "{path}: registers cannot be collected into {cname} cluster. {rspec} and {rspec1} have different indeces"
+                        "{}: registers cannot be collected into {cname} cluster. {rspec} and {rspec1} have different indeces",
+                        env.get("block_path").unwrap(),
                     ));
                 }
             } else {
@@ -1387,12 +1475,14 @@ fn collect_in_cluster(
             }
             if !check_offsets(&offsets, dim_increment) {
                 return Err(anyhow!(
-                    "{path}: registers cannot be collected into {cname} cluster. Different addressOffset increments in {rspec} registers"
+                    "{}: registers cannot be collected into {cname} cluster. Different addressOffset increments in {rspec} registers",
+                    env.get("block_path").unwrap(),
                 ));
             }
             if !bitmasks.iter().all(|&m| m == bitmasks[0]) {
                 return Err(anyhow!(
-                    "{path}: registers cannot be collected into {cname} cluster. Different bit masks in {rspec} registers"
+                    "{}: registers cannot be collected into {cname} cluster. Different bit masks in {rspec} registers",
+                    env.get("block_path").unwrap(),
                 ));
             }
         }
@@ -1400,7 +1490,8 @@ fn collect_in_cluster(
     }
     if rdict.is_empty() {
         return Err(anyhow!(
-            "{path}: registers cannot be collected into {cname} cluster. No matches found"
+            "{}: registers cannot be collected into {cname} cluster. No matches found",
+            env.get("block_path").unwrap(),
         ));
     }
     let address_offset = rdict
@@ -1424,7 +1515,7 @@ fn collect_in_cluster(
         for (_, (rmod, mut registers)) in rdict.into_iter() {
             let mut reg = registers.swap_remove(0).single();
             let rmod = rmod.hash()?;
-            reg.process(rmod, path, &config)
+            reg.process(rmod, &config, env.clone())
                 .with_context(|| format!("Processing register `{}`", reg.name))?;
             if let Some(name) = rmod.get_str("name")? {
                 reg.name = name.into();
@@ -1438,7 +1529,7 @@ fn collect_in_cluster(
         for (rspec, (rmod, mut registers)) in rdict.into_iter() {
             let mut reg = registers.swap_remove(0).single();
             let rmod = rmod.hash()?;
-            reg.process(rmod, path, &config)
+            reg.process(rmod, &config, env.clone())
                 .with_context(|| format!("Processing register `{}`", reg.name))?;
             reg.name = if let Some(name) = rmod.get_str("name")? {
                 name.into()
diff --git a/src/patch/register.rs b/src/patch/register.rs
index e6acf6d7..275108e5 100644
--- a/src/patch/register.rs
+++ b/src/patch/register.rs
@@ -12,7 +12,7 @@ use super::iterators::{MatchIter, Matched};
 use super::yaml_ext::{AsType, GetVal, ToYaml};
 use super::{
     check_offsets, common_description, make_dim_element, matchname, modify_dim_element, spec_ind,
-    Config, PatchResult, Spec, VAL_LVL,
+    update_env, Config, Env, PatchResult, Spec, VAL_LVL,
 };
 use super::{make_derived_enumerated_values, make_ev_array, make_ev_name, make_field};
 
@@ -38,10 +38,10 @@ impl RegisterInfoExt for RegisterInfo {
 /// Collecting methods for processing register contents
 pub trait RegisterExt {
     /// Work through a register, handling all fields
-    fn process(&mut self, rmod: &Hash, pname: &str, config: &Config) -> PatchResult;
+    fn process(&mut self, rmod: &Hash, config: &Config, env: Env) -> PatchResult;
 
     /// Add fname given by fadd to rtag
-    fn add_field(&mut self, fname: &str, fadd: &Hash) -> PatchResult;
+    fn add_field(&mut self, fname: &str, fadd: &Hash, env: &Env) -> PatchResult;
 
     /// Delete fields matched by fspec inside rtag
     fn delete_field(&mut self, fspec: &str) -> PatchResult;
@@ -55,20 +55,20 @@ pub trait RegisterExt {
     /// Work through a field, handling either an enum or a range
     fn process_field(
         &mut self,
-        pname: &str,
         fspec: &str,
         fmod: &Yaml,
         config: &Config,
+        env: &Env,
     ) -> PatchResult;
 
     /// Add an enumeratedValues given by field to all fspec in rtag
     fn process_field_enum(
         &mut self,
-        pname: &str,
         fspec: &str,
         fmod: &Hash,
         usage: Option<Usage>,
         config: &Config,
+        env: &Env,
     ) -> PatchResult;
 
     /// Set readAction for field
@@ -78,7 +78,7 @@ pub trait RegisterExt {
     fn set_field_modified_write_values(&mut self, fspec: &str, mwv: ModifiedWriteValues);
 
     /// Add a writeConstraint range given by field to all fspec in rtag
-    fn process_field_range(&mut self, pname: &str, fspec: &str, fmod: &[Yaml]) -> PatchResult;
+    fn process_field_range(&mut self, fspec: &str, fmod: &[Yaml], env: &Env) -> PatchResult;
 
     /// Delete substring from the beginning bitfield names inside rtag
     fn strip_start(&mut self, substr: &str) -> PatchResult;
@@ -87,7 +87,7 @@ pub trait RegisterExt {
     fn strip_end(&mut self, substr: &str) -> PatchResult;
 
     /// Modify fspec inside rtag according to fmod
-    fn modify_field(&mut self, fspec: &str, fmod: &Hash) -> PatchResult;
+    fn modify_field(&mut self, fspec: &str, fmod: &Hash, env: &Env) -> PatchResult;
 
     /// Merge all fspec in rtag.
     /// Support list of field to auto-merge, and dict with fspec or list of fspec
@@ -102,7 +102,12 @@ pub trait RegisterExt {
 }
 
 impl RegisterExt for Register {
-    fn process(&mut self, rmod: &Hash, pname: &str, config: &Config) -> PatchResult {
+    fn process(&mut self, rmod: &Hash, config: &Config, mut env: Env) -> PatchResult {
+        env.insert("register".into(), self.name.clone());
+        let rpath = format!("{}.{}", env.get("block_path").unwrap(), self.name);
+        env.insert("register_path".into(), rpath);
+        update_env(&mut env, rmod)?;
+
         if self.derived_from.is_some() {
             return Ok(());
         }
@@ -131,13 +136,13 @@ impl RegisterExt for Register {
         // Handle modifications
         for (fspec, fmod) in rmod.hash_iter("_modify") {
             let fspec = fspec.str()?;
-            self.modify_field(fspec, fmod.hash()?)
+            self.modify_field(fspec, fmod.hash()?, &env)
                 .with_context(|| format!("Modifying fields matched to `{fspec}`"))?;
         }
         // Handle additions
         for (fname, fadd) in rmod.hash_iter("_add") {
             let fname = fname.str()?;
-            self.add_field(fname, fadd.hash()?)
+            self.add_field(fname, fadd.hash()?, &env)
                 .with_context(|| format!("Adding field `{fname}`"))?;
         }
 
@@ -184,7 +189,7 @@ impl RegisterExt for Register {
             for (fspec, field) in rmod {
                 let fspec = fspec.str()?;
                 if !fspec.starts_with('_') {
-                    self.process_field(pname, fspec, field, config)
+                    self.process_field(fspec, field, config, &env)
                         .with_context(|| format!("Processing field matched to `{fspec}`"))?;
                 }
             }
@@ -227,9 +232,9 @@ impl RegisterExt for Register {
         Ok(())
     }
 
-    fn modify_field(&mut self, fspec: &str, fmod: &Hash) -> PatchResult {
+    fn modify_field(&mut self, fspec: &str, fmod: &Hash, env: &Env) -> PatchResult {
         let ftags = self.iter_fields(fspec).collect::<Vec<_>>();
-        let field_builder = make_field(fmod)?;
+        let field_builder = make_field(fmod, env)?;
         let dim = make_dim_element(fmod)?;
         if !ftags.is_empty() {
             for ftag in ftags {
@@ -268,14 +273,14 @@ impl RegisterExt for Register {
         Ok(())
     }
 
-    fn add_field(&mut self, fname: &str, fadd: &Hash) -> PatchResult {
+    fn add_field(&mut self, fname: &str, fadd: &Hash, env: &Env) -> PatchResult {
         if self.get_field(fname).is_some() {
             return Err(anyhow!(
                 "register {} already has a field {fname}",
                 self.name
             ));
         }
-        let fnew = make_field(fadd)?.name(fname.into()).build(VAL_LVL)?;
+        let fnew = make_field(fadd, env)?.name(fname.into()).build(VAL_LVL)?;
         let fnew = if let Some(dim) = make_dim_element(fadd)? {
             fnew.array(dim.build(VAL_LVL)?)
         } else {
@@ -507,10 +512,10 @@ impl RegisterExt for Register {
 
     fn process_field(
         &mut self,
-        pname: &str,
         fspec: &str,
         fmod: &Yaml,
         config: &Config,
+        env: &Env,
     ) -> PatchResult {
         const READ: phf::Map<&'static str, Option<ReadAction>> = phf::phf_map! {
             "_read" => None,
@@ -537,7 +542,7 @@ impl RegisterExt for Register {
                 let is_read = READ.keys().any(|key| fmod.contains_key(&key.to_yaml()));
                 let is_write = WRITE.keys().any(|key| fmod.contains_key(&key.to_yaml()));
                 if !is_read && !is_write {
-                    self.process_field_enum(pname, fspec, fmod, None, config)
+                    self.process_field_enum(fspec, fmod, None, config, env)
                         .with_context(|| "Adding read-write enumeratedValues")?;
                 } else {
                     if is_read {
@@ -545,11 +550,11 @@ impl RegisterExt for Register {
                             if let Some(fmod) = fmod.get_hash(key)? {
                                 if !fmod.is_empty() {
                                     self.process_field_enum(
-                                        pname,
                                         fspec,
                                         fmod,
                                         Some(Usage::Read),
                                         config,
+                                        env,
                                     )
                                     .with_context(|| "Adding read-only enumeratedValues")?;
                                 }
@@ -565,11 +570,11 @@ impl RegisterExt for Register {
                             if let Some(fmod) = fmod.get_hash(key)? {
                                 if !fmod.is_empty() {
                                     self.process_field_enum(
-                                        pname,
                                         fspec,
                                         fmod,
                                         Some(Usage::Write),
                                         config,
+                                        env,
                                     )
                                     .with_context(|| "Adding write-only enumeratedValues")?;
                                 }
@@ -582,7 +587,7 @@ impl RegisterExt for Register {
                 }
             }
             Yaml::Array(fmod) if fmod.len() == 2 => {
-                self.process_field_range(pname, fspec, fmod)
+                self.process_field_range(fspec, fmod, env)
                     .with_context(|| "Adding writeConstraint range")?;
             }
             _ => {}
@@ -608,11 +613,11 @@ impl RegisterExt for Register {
 
     fn process_field_enum(
         &mut self,
-        pname: &str,
         fspec: &str,
         mut fmod: &Hash,
         usage: Option<Usage>,
         config: &Config,
+        env: &Env,
     ) -> PatchResult {
         fn set_enum(
             f: &mut FieldInfo,
@@ -697,12 +702,21 @@ impl RegisterExt for Register {
                 .filter(|e| e.name.as_deref() == Some(d));
             let orig_usage = match (derived_enums.next(), derived_enums.next()) {
                 (Some(e), None) => e.usage().ok_or_else(|| {
-                    anyhow!("{pname}: multilevel derive for {d} is not supported")
+                    anyhow!(
+                        "{}: multilevel derive for {d} is not supported",
+                        env.get("register_path").unwrap()
+                    )
                 })?,
-                (None, _) => return Err(anyhow!("{pname}: enumeratedValues {d} can't be found")),
+                (None, _) => {
+                    return Err(anyhow!(
+                        "{}: enumeratedValues {d} can't be found",
+                        env.get("register_path").unwrap()
+                    ))
+                }
                 (Some(_), Some(_)) => {
                     return Err(anyhow!(
-                        "{pname}: enumeratedValues {d} was found multiple times"
+                        "{}: enumeratedValues {d} was found multiple times",
+                        env.get("register_path").unwrap(),
                     ));
                 }
             };
@@ -734,8 +748,8 @@ impl RegisterExt for Register {
                     return Ok(());
                 }
                 return Err(anyhow!(
-                    "Could not find field {pname}:{}:{fspec}. Present fields: {}.`",
-                    self.name,
+                    "Could not find field {}:{fspec}. Present fields: {}.`",
+                    env.get("register_path").unwrap(),
                     self.fields().map(|f| f.name.as_str()).join(", ")
                 ));
             }
@@ -786,7 +800,7 @@ impl RegisterExt for Register {
         Ok(())
     }
 
-    fn process_field_range(&mut self, pname: &str, fspec: &str, fmod: &[Yaml]) -> PatchResult {
+    fn process_field_range(&mut self, fspec: &str, fmod: &[Yaml], env: &Env) -> PatchResult {
         let mut set_any = false;
         let (fspec, ignore) = fspec.spec();
         for ftag in self.iter_fields(fspec) {
@@ -798,8 +812,8 @@ impl RegisterExt for Register {
         }
         if !ignore && !set_any {
             return Err(anyhow!(
-                "Could not find field {pname}:{}:{fspec}. Present fields: {}.`",
-                self.name,
+                "Could not find field {}:{fspec}. Present fields: {}.`",
+                env.get("register_path").unwrap(),
                 self.fields().map(|f| f.name.as_str()).join(", ")
             ));
         }