Điều gì nằm trong các loại bộ nhớ khác nhau của vi điều khiển?


25

Có các phân đoạn bộ nhớ khác nhau mà các loại dữ liệu khác nhau được đưa vào từ mã C sau khi biên dịch. Ví dụ: .text, .data, .bss, stack và heap. Tôi chỉ muốn biết mỗi phân đoạn này sẽ nằm ở đâu trong bộ nhớ vi điều khiển. Đó là, dữ liệu đi vào loại bộ nhớ nào, với các loại bộ nhớ là RAM, NVRAM, ROM, EEPROM, FLASH, v.v.

Tôi đã tìm thấy câu trả lời cho các câu hỏi tương tự ở đây, nhưng họ không giải thích được nội dung của từng loại bộ nhớ khác nhau là gì.

Bất kỳ loại trợ giúp được đánh giá cao. Cảm ơn trước!


1
NVRAM, ROM, EEPROM và Flash có khá nhiều tên gọi khác nhau cho cùng một thứ: bộ nhớ không bay hơi.
Lundin

Hơi khó hiểu cho câu hỏi, nhưng mã có thể (đặc biệt) tồn tại trong prety rất nhiều trong số này, đặc biệt nếu bạn xem xét sử dụng bản vá hoặc hiệu chuẩn. Đôi khi nó sẽ được di chuyển trước khi thực hiện, đôi khi được thực hiện tại chỗ.
Sean Houlihane

@SeanHoulihane OP đang hỏi về các bộ vi điều khiển, hầu như luôn luôn thực hiện ngoài Flash (bạn đã đủ điều kiện nhận xét của mình với ngoại lệ). Micro xử lý với MB của RAM bên ngoài chạy Linux ví dụ, sẽ sao chép chương trình của họ vào RAM để thực hiện chúng, có lẽ tắt một thẻ SD hoạt động như một khối lượng mountable.
tcrosley

@tcrosley Hiện tại có các bộ vi điều khiển với TCM và đôi khi các bộ vi điều khiển là một phần của SoC lớn hơn. Tôi cũng nghi ngờ có những trường hợp như các thiết bị eMMC trong đó mcu bootstraps tự chạy từ RAM ra khỏi bộ lưu trữ của chính nó (dựa trên bộ nhớ của một số điện thoại cứng cục gạch từ vài năm trước). Tôi đồng ý, đây không phải là câu trả lời trực tiếp - nhưng tôi nghĩ rất phù hợp rằng các ánh xạ điển hình không theo bất kỳ cách nào theo quy tắc cứng.
Sean Houlihane

1
không có quy tắc nào để kết nối thứ này với thứ khác, chắc chắn những thứ chỉ đọc như văn bản và Rodata sẽ lý tưởng muốn đi trong flash, nhưng .data và phần bù và kích thước của .bss cũng ở đó (và sau đó được sao chép bởi mã bootstrap). các thuật ngữ này (.text, v.v.) không liên quan gì đến vi điều khiển, đây là điều biên dịch / toolchain áp dụng cho tất cả các mục tiêu của trình biên dịch / công cụ. vào cuối ngày, lập trình viên quyết định mọi thứ sẽ đi đến đâu và thông báo cho chuỗi công cụ thông qua một tập lệnh liên kết thường.
old_timer

Câu trả lời:


38

.bản văn

Phân đoạn .text chứa mã thực tế và được lập trình vào bộ nhớ Flash cho các bộ vi điều khiển. Có thể có nhiều hơn một phân đoạn văn bản khi có nhiều khối bộ nhớ Flash không liền kề; ví dụ: vectơ bắt đầu và vectơ ngắt nằm ở đầu bộ nhớ và mã bắt đầu từ 0; hoặc các phần riêng biệt cho một chương trình bootstrap và chính.

.bss và .data

Có ba loại dữ liệu có thể được phân bổ bên ngoài cho một chức năng hoặc thủ tục; đầu tiên là dữ liệu chưa được khởi tạo (theo lịch sử gọi là .bss, cũng bao gồm dữ liệu khởi tạo 0) và dữ liệu thứ hai được khởi tạo (không phải bss) hoặc .data. Cái tên "bss" trong lịch sử xuất phát từ "Khối bắt đầu bằng biểu tượng", được sử dụng trong một trình biên dịch khoảng 60 năm trước. Cả hai khu vực này đều nằm trong RAM.

Khi một chương trình được biên dịch, các biến sẽ được phân bổ cho một trong hai khu vực chung này. Trong giai đoạn liên kết, tất cả các mục dữ liệu sẽ được thu thập cùng nhau. Tất cả các biến cần được khởi tạo sẽ có một phần bộ nhớ chương trình được đặt sang một bên để giữ các giá trị ban đầu và ngay trước khi hàm main () được gọi, các biến sẽ được khởi tạo, thông thường bởi một mô-đun gọi là crt0. Phần bss được khởi tạo cho tất cả các số không bằng cùng một mã khởi động.

Với một vài bộ vi điều khiển, có các hướng dẫn ngắn hơn cho phép truy cập vào trang đầu tiên (256 vị trí đầu tiên, đôi khi được gọi là trang 0) của RAM. Trình biên dịch cho các bộ xử lý này có thể bảo lưu một từ khóa như nearchỉ định các biến được đặt ở đó. Tương tự, cũng có những bộ vi điều khiển chỉ có thể tham chiếu các khu vực nhất định thông qua một thanh ghi con trỏ (yêu cầu thêm hướng dẫn) và các biến như vậy được chỉ định far. Cuối cùng, một số bộ xử lý có thể giải quyết một phần của bộ nhớ từng bit và trình biên dịch sẽ có cách để xác định điều đó (chẳng hạn như từ khóa bit).

Vì vậy, có thể có các phân đoạn bổ sung như .gầnbss và .gầndata, v.v., trong đó các biến này được thu thập.

.rodata

Loại dữ liệu thứ ba bên ngoài một chức năng hoặc thủ tục giống như các biến được khởi tạo, ngoại trừ nó là chỉ đọc và không thể được sửa đổi bởi chương trình. Trong ngôn ngữ C, các biến này được biểu thị bằng consttừ khóa. Chúng thường được lưu trữ như một phần của bộ nhớ flash chương trình. Đôi khi chúng được xác định là một phần của phân đoạn .rodata (dữ liệu chỉ đọc). Trên các vi điều khiển sử dụng kiến trúc Harvard , trình biên dịch phải sử dụng các hướng dẫn đặc biệt để truy cập các biến này.

chồng chất

Cả stack và heap đều được đặt trong RAM. Tùy thuộc vào kiến ​​trúc của bộ xử lý, ngăn xếp có thể lớn lên hoặc tăng xuống. Nếu nó lớn lên, nó sẽ được đặt ở dưới cùng của RAM. Nếu nó phát triển xuống, nó sẽ được đặt ở cuối RAM. Heap sẽ sử dụng RAM còn lại không được phân bổ cho các biến và phát triển theo hướng ngược lại của ngăn xếp. Kích thước tối đa của ngăn xếp và đống thường có thể được chỉ định làm tham số liên kết.

Các biến được đặt trên ngăn xếp là bất kỳ biến nào được xác định trong hàm hoặc thủ tục mà không có từ khóa static. Chúng từng được gọi là biến tự động ( autotừ khóa), nhưng từ khóa đó là không cần thiết. Trong lịch sử, autotồn tại bởi vì nó là một phần của ngôn ngữ B có trước C, và ở đó nó là cần thiết. Các tham số chức năng cũng được đặt trên ngăn xếp.

Dưới đây là cách bố trí điển hình cho RAM (giả sử không có phần 0 trang đặc biệt):

nhập mô tả hình ảnh ở đây

EEPROM, ROM và NVRAM

Trước khi bộ nhớ Flash xuất hiện, EEPROM (bộ nhớ chỉ đọc có thể lập trình có thể xóa bằng điện) đã được sử dụng để lưu trữ chương trình và dữ liệu const (phân đoạn .text và .rodata). Bây giờ chỉ có một lượng nhỏ (ví dụ: 2KB đến 8KB byte) của EEPROM, nếu có, và nó thường được sử dụng để lưu trữ dữ liệu cấu hình hoặc một lượng nhỏ dữ liệu khác cần được giữ lại khi mất điện chu kỳ. Chúng không được khai báo là biến trong chương trình, mà thay vào đó được ghi vào sử dụng các thanh ghi đặc biệt trong vi điều khiển. EEPROM cũng có thể được triển khai trong một chip riêng và được truy cập thông qua bus SPI hoặc I²C.

ROM về cơ bản giống như Flash, ngoại trừ nó được lập trình tại nhà máy (không được người dùng lập trình). Nó chỉ được sử dụng cho các thiết bị âm lượng rất cao.

NVRAM (RAM không bay hơi) là một thay thế cho EEPROM và thường được triển khai như một IC bên ngoài. RAM thông thường có thể được coi là không bay hơi nếu được dự phòng bằng pin; trong trường hợp đó không cần phương pháp truy cập đặc biệt.

Mặc dù dữ liệu có thể được lưu vào Flash, nhưng bộ nhớ Flash có số chu kỳ xóa / chương trình hạn chế (1000 đến 10.000) vì vậy nó không thực sự được thiết kế cho điều đó. Nó cũng yêu cầu xóa các khối bộ nhớ cùng một lúc, vì vậy việc cập nhật chỉ một vài byte là bất tiện. Nó dành cho mã và các biến chỉ đọc.

EEPROM có giới hạn cao hơn nhiều đối với các chu kỳ xóa / chương trình (100.000 đến 1.000.000) vì vậy sẽ tốt hơn cho mục đích này. Nếu có EEPROM có sẵn trên vi điều khiển và nó đủ lớn, đó là nơi bạn muốn lưu dữ liệu không bay hơi. Tuy nhiên, bạn cũng sẽ phải xóa các khối trước (thường là 4KB) trước khi viết.

Nếu không có EEPROM hoặc nó quá nhỏ, thì cần có chip bên ngoài. EEPROM 32KB chỉ 66 và có thể bị xóa / ghi thành 1.000.000 lần. Một NVRAM có cùng số thao tác xóa / chương trình đắt hơn nhiều (x10) NVRAM thường nhanh hơn để đọc so với EEPROM, nhưng chậm hơn khi viết. Chúng có thể được ghi vào một byte mỗi lần hoặc theo khối.

Một thay thế tốt hơn cho cả hai loại này là FRAM (RAM sắt điện), về cơ bản có chu kỳ ghi vô hạn (100 nghìn tỷ đồng) và không có độ trễ ghi. Nó có cùng mức giá với NVRAM, khoảng $ 5 cho 32KB.


Đó là một số thông tin hữu ích thực sự. Bạn có thể vui lòng cung cấp một tài liệu tham khảo để giải thích của bạn? Giống như sách giáo khoa hoặc tạp chí, trong trường hợp tôi muốn đọc thêm về điều này ..?
Soju T Varghese

một câu hỏi nữa, bạn có thể vui lòng đưa ra ý tưởng về ưu điểm hoặc nhược điểm của một bộ nhớ so với bộ nhớ khác (EEPROM, NVRAM và FLASH) không?
Soju T Varghese

Câu trả lời tốt đẹp. Tôi đã đăng một bài bổ sung tập trung chi tiết hơn về những gì diễn ra trong ngôn ngữ C một cách cụ thể.
Lundin

1
@SojuTVarghese Tôi đã cập nhật câu trả lời của mình và bao gồm một số thông tin về FRAM.
tcrosley

@Lundin chúng tôi đã sử dụng cùng tên phân khúc (ví dụ .rodata) để các câu trả lời bổ sung cho nhau một cách độc đáo.
tcrosley

21

Hệ thống nhúng thông thường:

Segment     Memory   Contents

.data       RAM      Explicitly initialized variables with static storage duration
.bss        RAM      Zero-initialized variables with static storage duration
.stack      RAM      Local variables and function call parameters
.heap       RAM      Dynamically allocated variables (usually not used in embedded systems)
.rodata     ROM      const variables with static storage duration. String literals.
.text       ROM      The program. Integer constants. Initializer lists.

Ngoài ra, thường có các phân đoạn flash riêng cho mã khởi động và vectơ ngắt.


Giải trình:

Một biến có thời lượng lưu trữ tĩnh nếu nó được khai báo là statichoặc nếu nó nằm trong phạm vi tệp (đôi khi được gọi một cách chậm chạp là "toàn cầu"). C có một quy tắc nói rằng tất cả các biến thời lượng lưu trữ tĩnh mà lập trình viên không khởi tạo rõ ràng phải được khởi tạo bằng không.

Mỗi biến thời lượng lưu trữ tĩnh được khởi tạo về 0, hoàn toàn hoặc rõ ràng, kết thúc bằng .bss. Trong khi những cái được khởi tạo rõ ràng với giá trị khác không thì kết thúc bằng .data.

Ví dụ:

static int a;                // .bss
static int b = 0;            // .bss      
int c;                       // .bss
static int d = 1;            // .data
int e = 1;                   // .data

void func (void)
{
  static int x;              // .bss
  static int y = 0;          // .bss
  static int z = 1;          // .data
  static int* ptr = NULL;    // .bss
}

Xin lưu ý rằng một thiết lập không chuẩn rất phổ biến cho các hệ thống nhúng là phải có "khởi động tối thiểu", điều đó có nghĩa là chương trình sẽ bỏ qua tất cả việc khởi tạo các đối tượng với thời lượng lưu trữ tĩnh. Do đó, có thể là khôn ngoan khi không bao giờ viết các chương trình dựa trên các giá trị khởi tạo của các biến đó, mà thay vào đó đặt chúng trong "thời gian chạy" trước khi chúng được sử dụng lần đầu tiên.

Ví dụ về các phân khúc khác:

const int a = 0;           // .rodata
const int b;               // .rodata (nonsense code but C allows it, unlike C++)
static const int c = 0;    // .rodata
static const int d = 1;    // .rodata

void func (int param)      // .stack
{
  int e;                   // .stack
  int f=0;                 // .stack
  int g=1;                 // .stack
  const int h=param;       // .stack
  static const int i=1;    // .rodata, static storage duration

  char* ptr;               // ptr goes to .stack
  ptr = malloc(1);         // pointed-at memory goes to .heap
}

Các biến có thể đi trên ngăn xếp thường có thể kết thúc trong các thanh ghi CPU trong quá trình tối ưu hóa. Theo nguyên tắc thông thường, bất kỳ biến nào không có địa chỉ của nó được lấy đều có thể được đặt trong thanh ghi CPU.

Lưu ý rằng các con trỏ phức tạp hơn một chút so với các biến khác, vì chúng cho phép hai loại khác nhau const, tùy thuộc vào việc dữ liệu trỏ phải ở chế độ chỉ đọc hay nếu chính con trỏ nên. Điều rất quan trọng là phải biết sự khác biệt để con trỏ của bạn không kết thúc RAM một cách tình cờ, khi bạn muốn chúng ở trong flash.

int* j=0;                  // .bss
const int* k=0;            // .bss, non-const pointer to const data
int* const l=0;            // .rodata, const pointer to non-const data
const int* const m=0;      // .rodata, const pointer to const data

void (*fptr1)(void);       // .bss
void (*const fptr2)(void); // .rodata
void (const* fptr3)(void); // invalid, doesn't make sense since functions can't be modified

Trong trường hợp các hằng số nguyên, danh sách khởi tạo, chuỗi ký tự, v.v., chúng có thể kết thúc bằng .text hoặc .rodata tùy thuộc vào trình biên dịch. Có khả năng, họ kết thúc như:

#define n 0                // .text
int o = 5;                 // 5 goes to .text (part of the instruction)
int p[] = {1,2,3};         // {1,2,3} goes to .text
char q[] = "hello";        // "hello" goes to .rodata

Tôi không hiểu, trong mã ví dụ đầu tiên của bạn, tại sao 'static int b = 0;' đi vào .bss và tại sao 'static int d = 1;' đi vào .data ..? Theo hiểu biết của tôi, cả hai đều là các biến tĩnh đã được lập trình viên khởi tạo .. vậy thì điều gì làm nên sự khác biệt? @Lundin
Soju T Varghese

2
@SojuTVarghese Vì dữ liệu .bss được khởi tạo thành 0 dưới dạng một khối; các giá trị cụ thể như d = 1 phải được lưu trữ trong flash.
tcrosley

@SojuTVarghese Đã thêm một số giải thích.
Lundin

@Lundin Ngoài ra, từ mã ví dụ cuối cùng của bạn, điều đó có nghĩa là tất cả các giá trị được khởi tạo đi vào .text hoặc .rodata và các biến tương ứng của chúng chỉ đi vào .bss hoặc .data? Nếu vậy, các biến và giá trị tương ứng của chúng được ánh xạ với nhau như thế nào (nghĩa là giữa các phân đoạn .bss / .data và .text / .rodata)?
Soju T Varghese

@SojuTVarghese Không, .datathường có một địa chỉ tải được gọi là flash, trong đó các giá trị ban đầu được lưu trữ và một địa chỉ ảo (không thực sự ảo trong vi điều khiển) trong RAM, nơi biến được lưu trữ trong khi thực thi. Trướcmain bắt đầu, các giá trị ban đầu được sao chép từ địa chỉ tải sang địa chỉ ảo. Bạn không cần lưu trữ số không, vì vậy .bsskhông cần lưu trữ giá trị ban đầu của nó. sourceware.org/binutils/docs/ld/ từ
starblue

1

Mặc dù bất kỳ dữ liệu nào cũng có thể đi vào bất kỳ bộ nhớ nào mà lập trình viên chọn, nhìn chung hệ thống hoạt động tốt nhất (và dự định sẽ được sử dụng) trong đó cấu hình sử dụng của dữ liệu được khớp với hồ sơ đọc / ghi của bộ nhớ.

Ví dụ mã chương trình là WFRM (viết ít đọc nhiều) và có rất nhiều mã. Điều này phù hợp với FLASH độc đáo. ROM OTOH là W một lần RM.

Stack và heap là nhỏ, với rất nhiều đọc và viết. Điều đó sẽ phù hợp nhất với RAM.

EEPROM sẽ không phù hợp với một trong những sử dụng tốt, nhưng nó phù hợp với hồ sơ của một lượng nhỏ dữ liệu liên tục trên các nguồn điện, vì vậy dữ liệu khởi tạo cụ thể của người dùng và có thể ghi lại kết quả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.