3 stable releases
1.1.0 | Aug 26, 2023 |
---|---|
1.0.1 | Aug 22, 2023 |
1.0.0 | Aug 21, 2023 |
0.1.0 |
|
#388 in Hardware support
35 downloads per month
39KB
563 lines
i.MX RT1060 Device Configuration Data (DCD) Generator
The i.MX RT1050/1060 series of MCUs feature a ROM bootloader. As a part of the boot process, it interprets the Device Configuration Data (DCD) section in the firmware image to perform limited initialization and validation of peripheral registers, e.g. to set up external memory controllers, before any ARM instructions from the firmware image are run.
This crate defines:
- Semantic descriptors of DCD commands.
- Serialization from a list of commands to the DCD binary (byte array).
A common use case / workflow:
- In the
build.rs
script of a firmware crate, define DCD commands and serialize them into a file (e.g.$OUT_DIR/dcd.bin
). - In the firmware itself, define a static byte array initialized with the contents of the DCD binary, which can be linked into the firmware image. (Shameless plug: static-include-bytes helps with this step.)
What does the DCD do exactly?
The DCD section in the firmware image is a serialized byte array of one or more commands:
-
Write: Write value (1/2/4-byte) to address.
*address = value
--- direct write*address &= !value
--- read-modify-write clear bits*address |= value
--- read-modify-write set bits
-
Check: Read from address until the value satisfies the condition or too many attempts.
(*address & mask) == 0
--- all clear(*address & mask) == mask
--- all set(*address & mask) != mask
--- any clear(*address & mask) != 0
--- any set
-
NOP: Ignored --- may behave as a small delay.
Reference: i.MX RT1060 Reference Manual (rev. 3), §9.7.2 .
See below for important details and caveats that affect the interpretation of DCD.
Usage
use imxrt_dcd as dcd;
use imxrt_ral as ral; // feature = "imxrt1062"
// RECOMMENDED: using imxrt-ral and convenience macros
let commands_macro = vec![
dcd::write_reg!(ral::ccm_analog, CCM_ANALOG, PLL_ARM, @BYPASS, BYPASS_CLK_SRC: CLK1),
dcd::check_all_clear!(ral::ccm, CCM, CDHIPR, @PERIPH_CLK_SEL_BUSY, @PERIPH2_CLK_SEL_BUSY),
];
// equivalent direct construction
let commands_direct = vec![
dcd::Command::Write(dcd::Write {
width: dcd::Width::B4,
op: dcd::WriteOp::Write,
address: 0x400D_8000,
value: 0x0001_4000,
}),
dcd::Command::Check(dcd::Check {
width: dcd::Width::B4,
cond: dcd::CheckCond::AllClear,
address: 0x400F_C048,
mask: (1 << 3) | (1 << 5),
count: None,
}),
];
assert_eq!(commands_macro, commands_direct);
// `serialize` into an `std::io::Write`
let mut dcd_bytes = vec![];
let num_bytes_written = dcd::serialize(&mut dcd_bytes, &commands_macro).expect("IO error");
assert_eq!(num_bytes_written, 28);
assert_eq!(
&dcd_bytes,
&[
// DCD header
0xD2, 0, 28, 0x41,
// write
0xCC, 0, 12, 0x04, 0x40, 0x0D, 0x80, 0x00, 0x00, 0x01, 0x40, 0x00,
// check
0xCF, 0, 12, 0x04, 0x40, 0x0F, 0xC0, 0x48, 0x00, 0x00, 0x00, 0x28,
]
);
Convenience Macros
To simplify the construction of commands, the feature "ral"
(on by default) provides convenience macros designed to work with register definitions in imxrt-ral
.
These macros share a common syntax as follows:
macro!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)
Where:
-
macro
can be:- Write:
write_reg
/set_reg
/clear_reg
- Check:
check_all_clear
/check_any_clear
/check_all_set
/check_any_set
- Write:
-
INSTANCE
should be a pointer-to-register-block, e.g. forral::ccm
this should beCCM
. -
Each
arg
can be:FIELD: value
=>(value << field::offset) & field::mask
- Same behavior as
ral-registers
. - Enumerators / named values of the field can be used directly in the
value
expression.
- Same behavior as
@FIELD
=>FIELD::mask
- Reads as "all (bits of)
FIELD
" - Useful for set, clear, and check commands working explicitly with field masks.
- Reads as "all (bits of)
- An arbitrary expression
- May directly refer to fields of the register (e.g.
(0b110 << FIELD1::offset) | FIELD2::mask
).
- May directly refer to fields of the register (e.g.
All args are then bitwise-OR'd together as the final value / mask of the command.
This syntax is inspired by (and is a superset of) write_reg!
and friends in imxrt-ral
(re-exporting ral-registers
), adapted for the limitations of DCD.
DCD Details and Caveats
DCD size limit
The DCD serialization format is 4-byte aligned with a 2-byte length field in its header. This allows the entire DCD block to be at most 65532 bytes long (all headers included). However, the boot ROM of a specific chip family may enforce a (much) shorter size limit. For RT1060 this is 1768 bytes.
This crate only enforces the 64 KiB length limit to keep the binary format valid, but does return the size of the serialized DCD so that the user may add a tighter check.
Write command compression
Multiple consecutive Write commands with the same bit width and operation (i.e. write/clear/set) can be merged (sharing the same command header) to save 4 bytes per extra command.
This crate automatically performs this compression during serialization. This may help meet the DCD size limit.
Valid Write command address ranges
The boot ROM of a specific chip family may only allow Write commands to a limited number of address ranges.
For example, the following are the valid address ranges for RT1060 (again from the reference manual):
Begin | End (inclusive) | Description |
---|---|---|
0x400A_4000 |
0x400A_7FFF |
IOMUX Control SNVS GPR |
0x400A_8000 |
0x400A_BFFF |
IOMUX Control SNVS |
0x400A_C000 |
0x400A_FFFF |
IOMUX Control GPR |
0x401F_8000 |
0x401F_BFFF |
IOMUX Control |
0x400D_8000 |
0x400D_BFFF |
CCM Analog |
0x400F_C000 |
0x400F_FFFF |
CCM |
0x402F_0000 |
0x402F_3FFF |
SEMC |
Writing to anywhere outside these ranges will cause the boot ROM to immediately abandon interpreting the rest of your DCD.
This crate does not enforce any address range limitations. The user is expected to provide valid write addresses for the target chip family.
Check command polling count
The Check command may specify one of the following:
- Omitted max polling count: ROM will poll indefinitely as long as the condition remains unsatisfied.
- max polling count == 0: Does not poll at all --- equivalent to NOP.
- max polling count > 0: If the max polling count is hit, the boot ROM will immediately abandon interpreting the rest of your DCD.
Note that (through my limited experimentation) the boot ROM does not seem to limit the address range of Check commands.
Tips
Command Combos
-
ral::modify_reg!(..., FIELD1: value1, FIELD2: value2)
can be approximated using the following DCD command sequence:dcd::clear_reg!(..., @FIELD1, @FIELD2)
dcd::set_reg!(..., FIELD1: value1, FIELD2: value2)
- NOTE: This might trigger additional side effects due to two read-modify-write cycles. This is the minimum due to the lack of temporary variables in DCD.
-
ral::read_reg!(..., FIELD == value)
can be approximated using the following DCD command sequence:dcd::check_all_set!(..., FIELD: value)
dcd::check_all_clear!(..., FIELD: !value)
- NOTE: This does NOT work if the register can change between the two checks. Multiple fields in the same register can be checked with the same 2 commands though.
Dependencies
~425KB