Here’s the corrected version of your script, keeping the format intact:
---
Welcome to part 4.
In the last part, we discussed the internal architecture of the STM32F411
microcontroller. In this part, we will explore the memory map of this
microcontroller.
Using the example of a restaurant: there are several tables in a restaurant that
are serviceable. A restaurant may have 10 or 15 tables, depending on its size. If
we add more tables, it would become congested, right?
So, there is a limit to the number of addressable tables. Similarly, in the case of
a microcontroller, the CPU can service only a limited range of addresses.
We previously discussed 8-bit controllers, 16-bit controllers, and 32-bit
controllers. The controller we are using now is a 32-bit microcontroller. So, our
32-bit microcontroller can service \(2^{32}\) different addresses. \(2^{32}\)
equals a 4 GB address space.
That is **4,294,967,296** in decimal. The CPU can access any address within this
range to fetch data stored in memory. In hexadecimal, the address range is from
`0x00000000` to `0xFFFFFFFF`.
Theoretically, the CPU can access this entire 4 GB address space. If it needs to
fetch data from a particular memory location, that location must fall within this 4
GB region.
Similarly, if we need to retrieve an instruction or data from SRAM, that SRAM
region will also fall within this address range. If we are communicating with
peripherals via the system bus, the peripheral addresses will also reside within
this 4 GB space.
In the STM32F411 microcontroller, this 4 GB memory space is divided into 8 parts of
0.5 GB each. The first 512 MB belongs to Block 0, the next 512 MB to Block 1, and
so on, up to Block 7, starting from Base 0.
So, what is mapped in each block? Let’s check the details in the reference manual.
Now, turning to the STM32F411 microcontroller's datasheet, let’s look at Section 5:
**Memory Mapping**. In this section, they’ve detailed the overall memory map of the
microcontroller. Within the range from `0x00000000` to `0xFFFFFFFF`, the 4 GB
addressable memory space is divided into 8 blocks of 512 MB each, just as we
discussed earlier.
If we count here, there are 1, 2, 3, 4, 5, and 6 blocks, which total 3 GB. The
remaining 1.5 GB (3 × 512 MB blocks) is reserved. The reason for this reserved area
will be explained later.
For Block 0, the **Code Memory** is allocated 512 MB. However, our microcontroller
has only 512 KB of Flash memory, as mentioned in the datasheet.
The 512 MB allocation in the memory map is the theoretical size allowed by the ARM
CPU architecture.
---
The size of flash memory increases in the higher series of microcontrollers.
However, in this controller, only 512 KB of flash memory is used within the entire
512 MB block.
If we look closely, there is an address space that can be aliased to flash memory,
system memory, or SRAM, depending on the **BOOT pins** configuration. When you
resolve these numbers, you’ll find that 512 KB of memory is aliased to flash memory
or SRAM based on the state of the BOOT pins.
This entire table corresponds to the 512 MB Code block. The alias to flash memory
means that the flash memory starts at address `0x08000000` and extends up to 512
KB. Beyond that, there is **system memory**, which we’ll discuss shortly, followed
by **option bytes**—we will also cover those. Additionally, there are regions of
reserved memory at various locations.
When we resolve all these, it becomes clear that the flash memory starts at
`0x08000000`. However, this memory is aliased by the memory regions shown below,
meaning they are **virtual addresses**.
When you power on the microcontroller, it needs to boot up. It can boot from one of
three different memory regions, determined by the BOOT pins. The microcontroller
has two BOOT pins: **BOOT0** and **BOOT1**. These pins decide the boot
configuration.
If we refer to the datasheet again, in Section 3.13: **Boot Modes**, we see that
the boot modes allow the selection of one out of three options:
1. Boot from flash memory
2. Boot from system memory
3. Boot from embedded SRAM
So now you know about boot modes. What are BOOT pins?
STM32F4 microcontrollers include a special mechanism to enable booting from other
memories, such as the internal SRAM.
---
STM32F4 microcontrollers implement a special mechanism to boot from other memories,
such as internal SRAM.
On this board, there are two pins called **BOOT0** and **BOOT1**. By configuring
these pins as high or low, you can select the boot mode.
- If you want to boot from the main flash memory, simply set the BOOT0 pin to
**Low**. The state of BOOT1 does not matter in this case. As soon as the
microcontroller starts up, it will execute code from the flash memory.
- If the BOOT0 pin is set to **High**, the microcontroller will boot from **system
memory**.
- If both BOOT0 and BOOT1 are set to **High**, the microcontroller will boot from
**SRAM**.
By default, microcontrollers typically boot from flash memory. On our development
board, as per the schematics, the BOOT0 pin is configured as Low, so the system
boots from the main flash memory.
### What is system memory?
System memory is a special region that contains the **bootloader**.
According to the datasheet, the size of the system memory is **30 KB**, while the
flash memory is **512 KB**.
### What are the advantages of booting from system memory?
System memory holds the bootloader, which plays a critical role in firmware
upgrades. For example, in devices like set-top boxes or DVD players, there is often
a USB port that allows you to update the firmware.
The bootloader enables this special mode, allowing the firmware of the
microcontroller to be updated securely and safely.
In normal operation, when the application code is running from flash memory,
upgrading the firmware is nearly impossible due to the risk of overwriting the
active code.
However, by jumping to the bootloader, the microcontroller enters a special mode
designed specifically for firmware upgrades, ensuring the process is both safe and
secure.
The bootloader includes security features to authenticate the firmware and verify
its integrity.
Using the bootloader, you can safely replace the old firmware with a new version.
While there are many other functionalities the bootloader offers, we won’t dive
into those details today.
Earlier, I mentioned USB as an example for updating firmware. However, according to
the datasheet, there are several methods available for reprogramming or upgrading
the firmware. These include **USART**, **USB**, **I2C**, and **SPI**. Multiple
options are provided to ensure flexibility in firmware updates.
---
Here’s the revised version of your script:
---
The system memory is essential for updating firmware.
Next, we have **reserved memory** with a size of 125 MB. Any read or write
operation in this region will have no effect.
This reserved memory exists because, while the CPU architecture allows these
addresses to be used, the microcontroller vendor, ST, didn’t physically realize
this memory. Therefore, it is marked as reserved.
After the reserved memory, we have **Flash memory**, which is 512 KB in size.
Beyond that, there’s another **300 MB of reserved memory**. Then, we have **System
memory** with a size of 30 KB, along with some space reserved for future expansion.
Finally, there are the **Option Bytes**.
This completes the description of the entire 512 MB block. We’ve covered everything
except Option Bytes. So, what are Option Bytes?
You can find information about Option Bytes in the datasheet. These are special
configurations set by the end user depending on application requirements. They
enable different modes, including protection levels, to be configured.
One of the modes is **Level 0 – No Protection**.
In this mode:
- You can write code to the flash, debug it, and erase the flash.
- Anyone can read the flash memory and decompile your code.
- The microcontroller doesn’t offer any protection for your application.
If you sell a product with **No Protection** enabled, your flash memory can be read
and your proprietary code exposed. This is why Level 0 is not recommended for
commercial products.
By setting the protection level to **Level 1**, you can enable some security.
- In Level 1:
- Code execution from flash works as expected, but debugging is disabled.
- Although flash memory cannot be read, it can still be erased and reprogrammed.
The highest level of protection is **Level 2 – Chip Protection**.
- In Level 2:
- Debugging is entirely disabled.
- Booting from SRAM is also disabled.
- This ensures the highest level of security for your application.
Option Bytes occupy a small memory region from `0xC000` to `0xC007`, a total of **8
bytes**. Based on the values configured in this region, protection levels are set,
and additional features, like the watchdog timer, can be enabled.
Let’s not go into further details about the other features today.
---
Here’s the revised version of the script:
---
So, **Option Bytes** are optional protection features that can be enabled as
needed.
Next, let’s move on to **Block 1 Memory**, which is dedicated to SRAM. Like the
previous block, this block is 512 MB in size. However, just as with flash memory,
the entire block isn’t utilized. The SRAM size for this microcontroller is **128
KB**, starting at address `0x20000000`. If you add 128 KB to this address, the
remaining portion of the 512 MB block is marked as **reserved**.
Why is it mentioned as **aliased by bit-banding**?
Bit-banding is an interesting feature, and I’ll explain it in detail at the end of
this video. For now, just know that this feature allows efficient manipulation of
individual bits in SRAM and peripheral regions.
Now, let’s move on to **Block 2 Memory**, another 512 MB block, which is dedicated
to **Peripherals**. This block starts at address `0x40000000` and spans up to 512
MB.
There is a small mistake in the document here: it shows both **Peripherals** and
**CPU Internal Peripherals** within this block. For now, we’ll focus only on the
**Block 2 Peripherals**.
In this block, the **APB** and **AHB peripherals** are mapped. This includes:
- **APB1 peripherals**
- **APB2 peripherals**
- **AHB1 peripherals**
- **AHB2 peripherals**
### Example:
For instance, the address range starting from `0x40000000` is mapped to the **Timer
2 Peripheral**.
- The addressable range for Timer 2 is from `0x40000000` to `0x400003FF`, which is
1 KB.
- To interact with Timer 2, you can access its registers within this address range.
- Similarly, to manipulate the **Timer 3 Peripheral**, you would interact with its
registers in its dedicated address range.
Every peripheral in this block is assigned a specific memory range for interaction.
We’ll discuss peripherals in greater detail in a separate video.
Now, within this block:
- **APB2 peripherals** include components like USART.
- **AHB1 peripherals** include **GPIO ports** (GPIOA, GPIOB, GPIOC, GPIOD, GPIOE,
and others). These peripherals are connected to the AHB1 bus.
- **AHB2 peripherals** include components like USB, which you can access using
addresses within this range.
For example, if you want to interact with GPIOA, you will use its specific address
range within the **AHB1 bus**. GPIOA itself contains many registers that you’ll
need to manipulate for various functionalities. We’ll cover GPIOs in detail in a
dedicated video.
Up to now, we’ve discussed how peripherals are mapped within the **Block 2
addressable memory**.
### Memory-Mapped I/O
The concept of addressing a peripheral to read from or write to it—just like
addressing memory—is called **Memory-Mapped I/O**.
---
Here’s the revised version with improved clarity while keeping the structure
intact:
---
Next, there is a Reserved section of size 1.5 GB. Along with an additional 0.5 GB,
the total comes to 2 GB. According to the Programmer’s Manual of the STM32F4
family, 1 GB is allocated for external devices and another 1 GB for external RAM.
Our memory map states that 1.5 GB cannot be used. This suggests that 0.5 GB of
space might be available for addressing an external device. However, the datasheet
indicates that this block is not utilized. So, while it is not technically
reserved, it remains unused. In theory, you could connect external memory to this
address, but there are several limitations, which we won’t discuss in this series.
What we need to focus on is understanding how to use the peripheral section, SRAM
section, and flash memory section.
As I mentioned at the beginning of the video, let's now talk about **bit banding**.
Referring to the Programmer’s Manual, each **word (32-bit)** in the **bit-band
alias region** maps to a specific bit in the **bit-band region**.
The alias region is virtual memory, whereas the bit-band region is actual physical
memory.
Here’s an example:
The SRAM region starts from **0x20000000** and increments by **1 byte**. Since a
byte consists of **8 bits**, address **0x20000000** contains 8 individual bits (0
to 7). A **word** (4 bytes) consists of **32 bits**.
Now, let's assume that **32 LEDs** are connected to these 32 bits.
If you need to turn **only one LED on or off**, you would typically:
1. Read the entire byte from memory.
2. Modify the required bit.
3. Write the updated byte back to the register.
This read-modify-write cycle introduces a problem: it can be interrupted by a
higher-priority event, such as an **interrupt**. If an interrupt occurs in the
middle of modifying data, it could lead to unintended behavior. I’ll discuss these
issues in more detail when we cover **OS concepts and ISRs (Interrupt Service
Routines).**
By default, the smallest unit we can read from memory is **1 byte (8 bits)**.
However, what if there were a way to **set or clear just a single bit in one
instruction?** Wouldn’t that be more efficient? This is where **bit-banding** comes
in—it allows **atomic operations**, preventing higher-priority interrupts from
interfering.
For example:
At address **0x20000000** (which is in the bit-band region), if we want to modify
bit 0, we can write to its alias address in the **bit-band alias region**, which
starts at **0x22000000** in virtual memory.
The alias address follows a pattern:
- **0x22000000** corresponds to bit **0** of **0x20000000**.
- The next alias address is **0x22000004**, which maps to bit **1** of
**0x20000000**.
- Each bit’s alias address increments by **4 bytes**.
So, instead of modifying the entire byte, we can directly write **0 or 1** to the
corresponding alias address, and the bit in the bit-band region will be updated.
This is the concept of **bit-banding**, and we can explore it further in future
videos.
Now, in the LED example, I mentioned **GPIO** as an application. However, that’s
not entirely accurate. GPIO has a dedicated **bit set/reset register (GPIOx_BSRR)**
that can manipulate individual bits even faster than the bit-banding method.
We will discuss GPIO in a separate video, but for today, just remember that bit-
banding works using a **virtual address that lets you set or clear a bit in actual
physical memory.**
---
### Block 7: Cortex-M4 CPU Internal Peripherals
The final **512 MB block (Block 7)** is reserved for the **Cortex-M4 CPU’s internal
peripherals**. This address space is managed by the CPU to handle its internal
operations. Like other blocks, many sections are **reserved**, while specific areas
are mapped to **internal peripherals**.
You can see these **internal CPU peripherals** in the block diagram. Some examples
include:
- **Nested Vector Interrupt Controller (NVIC)**
- **Memory Protection Unit (MPU)**
- **DSP Unit**
- **Floating Point Unit (FPU)**
- **Serial Wire Debugger (SWD)**
These peripherals help manage various CPU functions.
---
Next, there is a Reserved section of size 1.5 GB. Along with an additional 0.5 GB,
the total comes to 2 GB. According to the Programmer’s Manual of the STM32F4
family, 1 GB is allocated for external devices and another 1 GB for external RAM.
Our memory map states that 1.5 GB cannot be used. This suggests that 0.5 GB of
space might be available for addressing an external device. However, the datasheet
indicates that this block is not utilized. So, while it is not technically
reserved, it remains unused. In theory, you could connect external memory to this
address, but there are several limitations, which we won’t discuss in this series.
What we need to focus on is understanding how to use the peripheral section, SRAM
section, and flash memory section.
As I mentioned at the beginning of the video, let's now talk about bit banding.
Referring to the Programmer’s Manual, each word (32-bit) in the bit-band alias
region maps to a specific bit in the bit-band region.
The alias region is virtual memory, whereas the bit-band region is actual physical
memory.
Here’s an example:
The SRAM region starts from 0x20000000 and increments by 1 byte. Since a byte
consists of 8 bits, address 0x20000000 contains 8 individual bits (0 to 7). A word
(4 bytes) consists of 32 bits.
Now, let's assume that 32 LEDs are connected to these 32 bits.
If you need to turn only one LED on or off, you would typically:
Read the entire byte from memory.
Modify the required bit.
Write the updated byte back to the register.
This read-modify-write cycle introduces a problem: it can be interrupted by a
higher-priority event, such as an interrupt. If an interrupt occurs in the middle
of modifying data, it could lead to unintended behavior. I’ll discuss these issues
in more detail when we cover OS concepts and ISRs (Interrupt Service Routines).
By default, the smallest unit we can read from memory is 1 byte (8 bits). However,
what if there were a way to set or clear just a single bit in one instruction?
Wouldn’t that be more efficient? This is where bit-banding comes in—it allows
atomic operations, preventing higher-priority interrupts from interfering.
For example:
At address 0x20000000 (which is in the bit-band region), if we want to modify bit
0, we can write to its alias address in the bit-band alias region, which starts at
0x22000000 in virtual memory.
The alias address follows a pattern:
0x22000000 corresponds to bit 0 of 0x20000000.
The next alias address is 0x22000004, which maps to bit 1 of 0x20000000.
Each bit’s alias address increments by 4 bytes.
So, instead of modifying the entire byte, we can directly write 0 or 1 to the
corresponding alias address, and the bit in the bit-band region will be updated.
This is the concept of bit-banding, and we can explore it further in future videos.
Now, in the LED example, I mentioned GPIO as an application. However, that’s not
entirely accurate. GPIO has a dedicated bit set/reset register (GPIOx_BSRR) that
can manipulate individual bits even faster than the bit-banding method.
We will discuss GPIO in a separate video, but for today, just remember that bit-
banding works using a virtual address that lets you set or clear a bit in actual
physical memory.
Block 7: Cortex-M4 CPU Internal Peripherals
The final 512 MB block (Block 7) is reserved for the Cortex-M4 CPU’s internal
peripherals. This address space is managed by the CPU to handle its internal
operations. Like other blocks, many sections are reserved, while specific areas are
mapped to internal peripherals.
You can see these internal CPU peripherals in the block diagram. Some examples
include:
Nested Vector Interrupt Controller (NVIC)
Memory Protection Unit (MPU)
DSP Unit
Floating Point Unit (FPU)
Serial Wire Debugger (SWD)
These peripherals help manage various CPU functions.
### Memory Endianness
Before we conclude, let’s cover one last important concept: **Memory Endianness.**
What is **Endianness**?
Let’s take an example:
Assume an address **0x08000000**. Since our ARM CPU is **byte-addressable**, the
next addresses will be:
- **0x08000001**
- **0x08000002**
- **0x08000003**, and so on.
Now, suppose we store the 4-byte **hexadecimal number 0x12345678** at
**0x08000000**.
How will these bytes be stored?
That depends on whether the CPU follows **Little Endian** or **Big Endian** format.
Our STM32F411 microcontroller, based on the **ARM Cortex-M4**, follows the **Little
Endian** format.
- The **Least Significant Byte (LSB) → 0x78** is stored at **0x08000000**.
- The next byte **0x56** is stored at **0x08000001**.
- The byte **0x34** is stored at **0x08000002**.
- The **Most Significant Byte (MSB) → 0x12** is stored at **0x08000003**.
If the processor were **Big Endian**, the order would be reversed:
- **0x12** at **0x08000000**
- **0x34** at **0x08000001**
- **0x56** at **0x08000002**
- **0x78** at **0x08000003**
This **Endianness** concept is particularly important when communicating with
**external devices**. Some devices send data in **Big Endian** format, while our
processor uses **Little Endian**.
Endianness also plays a role in **networking protocols**, where data needs to be
interpreted correctly regardless of the system architecture.
---
### Conclusion
Alright, that brings us to the end of this video! We covered:
✅ The STM32F411 memory map
✅ Bit-banding and its advantages
✅ Cortex-M4 internal peripherals
✅ Memory endianness and its significance
Thanks for watching! See you in the next video! 🎯🔧🚀