1. Concept of address
1) Physical address: The address transmitted by the CPU address bus, whose specific meaning is controlled by the hardware circuit. A large part of the physical address is reserved for the memory in the memory stick, but it is also often mapped to other memories (such as video memory, BIOS, etc.). After the virtual address in the program instruction is segment mapped and page mapped, a physical address is generated, and this physical address is placed on the address line of the CPU.
The physical address space is partly used for physical RAM (memory) and partly for the bus, which is determined by the hardware design. Therefore, in the x86 processor with a 32-bit address line, the physical address space is 2 to the power of 32, that is, 4GB, but the physical RAM generally cannot reach 4GB because there is still a part to be used for the bus (many other devices are hung on the bus). In PCs, the low-end physical address is generally used for RAM, and the high-end physical address is used for the bus.
2) Bus address: The address line of the bus or the signal generated in the address cycle. Peripherals use bus addresses, and the CPU uses physical addresses.
The relationship between physical addresses and bus addresses is determined by the design of the system. On the x86 platform, the physical address is the bus address, because they share the same address space - this sentence is a bit difficult to understand, see "Independent Addressing" below for details. On other platforms, conversion/mapping may be required. For example: the CPU needs to access the unit with physical address 0xfa, then on the x86 platform, an access to address 0xfa on the PCI bus will be generated. Because the physical address and the bus address are the same, it is impossible to determine where this address is used by looking at it. It may be in the memory, or a storage unit on a card, or there may even be no corresponding storage at this address.
3) Virtual address: Modern operating systems generally use a virtual memory management mechanism, which requires the support of MMU (Memory Management Unit). MMU is usually part of the CPU. If the processor does not have an MMU, or has an MMU but is not enabled, the memory address issued by the CPU execution unit will be directly transmitted to the chip pins and received by the memory chip (physical memory). This is called a physical address. If the processor has MMU enabled, the memory address issued by the CPU execution unit will be intercepted by the MMU. The address from the CPU to the MMU is called a virtual address, and the MMU translates this address into another address and sends it to the external address pin of the CPU chip, that is, mapping the virtual address to a physical address.
In Linux, the 4GB (virtual) memory of a process is divided into user space and kernel space. The user space is distributed from 0 to 3GB (i.e., PAGE_OFFSET, which is equal to 0xC0 in 0X86), and the remaining 1G is the kernel space. Programmers can only use virtual addresses. Each process in the system has its own private user space (0 to 3G), which is invisible to other processes in the system.
The address when the CPU issues an instruction fetch request is the virtual address of the current context. The MMU then finds the physical address of this virtual address from the page table to complete the instruction fetch. Similarly, the virtual address is used to read data, such as mov ax, var. When compiling, var is a virtual address, and the MMU also finds the physical address from the page table, and then generates the bus timing to complete the data fetch.
(ii) Addressing methods
1) Peripherals are controlled by reading and writing registers on the device. Peripheral registers are also called "I/O ports", and there are two addressing methods for IO ports: independent addressing and unified addressing.
Unified addressing: The IO registers (i.e., IO ports) in the peripheral interface are treated the same as the main memory units. Each port occupies the address of a storage unit, and a part of the main memory is allocated as the IO address space. For example, in PDP-11, the highest 4K main memory is used as the IO device register address. The port occupies the address space of the memory, which reduces the storage capacity.
Unified addressing is also called "I/O memory" mode. The peripheral registers are located in the "memory space" (many peripherals have their own memory and buffer, and the peripheral registers and memory are collectively referred to as "I/O space").
For example, Samsung's S3C2440 is a 32-bit ARM processor, and its 4GB address space is divided up by peripherals, RAM, etc.:
0x8 1 LED 8*8 dot matrix address
0x4800 0 ~ 0x6 0 SFR (special register) address space
0x3800 1002 Keyboard address
0x3 0 ~ 0x3400 0 SDRAM space
0x2 0020 ~ 0x2 002e IDE
0x1900 0300 CS8900
Independent addressing (single addressing): IO address and storage address are separately and independently addressed, and I/0 port address does not occupy the address range of storage space. In this way, there is another IO address in the system that is unrelated to storage address, and CPU must also have IO instructions (IN, OUT, etc.) and control logic dedicated to input and output operations. Under independent addressing, when an address comes over the address bus, the device does not know whether it is for IO port or for memory, so the processor uses two sets of control signals, MEMR/MEMW and IOR/IOW, to achieve different addressing of I/O port and memory. For example, Intel 80x86 uses separate addressing, and CPU memory and I/O are addressed together, which means that the address of part of memory overlaps with I/O address.
Independent addressing is also called "I/O port" mode, and peripheral registers are located in "I/O (address) space".
For x86 architecture, it is accessed through IN/OUT instructions. The PC architecture has a total of 65536 8-bit I/O ports, forming a 64K I/O address space, numbered from 0 to 0xFFFF, with 16 bits, and 80x86 uses the lower 16-bit address lines A0-A15 for addressing. Two consecutive 8-bit ports can form a 16-bit port, and four consecutive ports form a 32-bit port. The I/O address space and the CPU's physical address space are two different concepts. For example, the I/O address space is 64K, and the physical address space of a 32-bit CPU is 4G. For example, under Intel 8086+Redhat9.0, use "more /proc/ioports" to see:
0-001f : dma1 |
However, Intel x86 platforms generally use a technology called memory mapping (MMIO), which is part of the PCI specification. IO device ports are mapped to memory space. After mapping, the CPU accesses IO ports just like accessing memory. See the typical memory address allocation table for x86/x64 systems given in Intel TA 719 document:
System resource usage
BIOS 1M Local APIC 4K Chipset reserves 2M IO APIC 4K PCI device 256M PCI Express Device 256M PCI device (optional) 256M Display frame buffer 16M TSEG 1M |
For a given system, it is either independent addressing or unified addressing, and which one is used depends on the CPU architecture. For example, PowerPC, m68k, etc. use unified addressing, while X86, etc. use independent addressing, and there is the concept of IO space. At present, most embedded microcontrollers such as ARM, PowerPC, etc. do not provide I/O space, only memory space, which can be directly accessed by address and pointer. But for the Linux kernel, it may be used for different CPUs, so it must consider both methods, so it adopts a new method, referring to I/O ports based on I/O mapping or memory mapping as "I/O region". No matter which method you use, you must first apply for the IO region: request_resource(), and release it at the end: release_resource().
2) Access to peripherals
1. The process of accessing I/O memory is: request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() ->release_mem_region().
As mentioned before, IO memory is a concept under unified addressing. For unified addressing, IO address space is part of physical main memory. For programming, we can only operate virtual memory. Therefore, the first step of access is to map the physical address of the device to the virtual address. In Linux 2.6, use ioremap():
void *ioremap(unsigned long offset, unsigned long size);
Then, we can directly access these addresses through pointers, but we can also use a set of Linux kernel functions to read and write:
ioread8(), iowrite16(), ioread8_rep(), iowrite8_rep()......
2. Accessing I/O ports
There are two ways to access IO ports: I/O-mapped and memory-mapped. The former does not map to memory space, but directly uses functions such as intb()/outb() to read and write IO ports; the latter MMIO first maps the IO port to IO memory ("memory space"), and then uses the function to access IO memory to access the IO port.
void ioport_map(unsigned long port, unsigned int count);
Through this function, count consecutive IO ports starting from port can be mapped to a "memory space", and then these IO ports can be accessed at the address returned by it like accessing IO memory.
(III) IO ports and IO memory under Linux
There are two ways for the CPU to address the physical address of peripheral ports: one is IO mapping, and the other is memory mapping.
Linux refers to IO ports based on IO mapping and memory mapping as IO regions.
IO region is still an IO resource, so it can still be described by the resource structure type.
Linux manages IO region:
1) request_region()
Assign a given range of IO ports to an IO device.
2) check_region()
Checks whether a given range of IO ports are free, or whether some of them are already assigned to an IO device.
3) release_region()
Releases the given range of IO ports previously allocated to an IO device.
In Linux, you can access IO ports through the following auxiliary functions:
inb(),inw(),inl(),outb(),outw(),outl()
"b", "w", and "l" represent 8 bits, 16 bits, and 32 bits respectively.
Access to IO memory resources
1) request_mem_region()
Requests allocation of specified IO memory resources.
2) check_mem_region()
Checks whether the specified IO memory resource is occupied.
3) release_mem_region()
Releases the specified IO memory resources.
The start address parameter passed to the function is the physical address of the memory area (the above function parameter table has been omitted).
Driver developers can treat memory-mapped IO ports and peripheral memory as IO memory resources.
ioremap() is used to map the physical address of IO resources to the kernel virtual address space (3GB - 4GB). The parameter addr is a pointer to the kernel virtual address.
In Linux, IO memory resources can be accessed through the following auxiliary functions:
readb(),readw(),readl(),writeb(),writew(),writel().
Linux defines the global variables ioport_resource and iomem_resource in the kernel/resource.c file to describe the entire IO port space based on IO mapping and the IO memory resource space based on memory mapping (including IO ports and peripheral memory) respectively.
1) About IO and memory space:
In X86 processors, there is the concept of I/O space. I/O space is relative to memory space and is accessed through specific instructions in and out. The port number identifies the register address of the peripheral. The format of in and out instructions in Intel syntax is:
IN accumulator, {port number│DX}
OUT {port number│DX}, accumulator
At present, most embedded microcontrollers such as ARM, PowerPC, etc. do not provide I/O space, but only memory space. Memory space can be accessed directly through addresses and pointers. Programs and variables and other data used in program operation exist in memory space.
Even in X86 processors, although I/O space is provided, if we design the circuit board ourselves, peripherals can still be mounted only in memory space. At this time, the CPU can access the peripheral I/O port like accessing a memory unit, without setting up special I/O instructions. Therefore, memory space is necessary, and I/O space is optional.
(2) inb and outb:
In Linux device drivers, it is advisable to use the functions provided by the Linux kernel to access ports located in the I/O space. These functions include:
· Read and write byte ports (8 bits wide)
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
· Read and write word ports (16 bits wide)
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
· Read and write long word ports (32 bits wide)
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
· Read and write a string of bytes
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
· insb() reads count bytes starting from port port and writes the read result to the memory pointed to by addr; outsb() writes count bytes of the memory pointed to by addr continuously to the port starting from port.
· Read and write a string of words
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
· Read and write a string of long words
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
The type of I/O port number port in the above functions is highly dependent on the specific hardware platform, so just unsigned is written.
The above knowledge points are taken from: http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/20-2556.html
I won't embarrass myself here, because the above ones are already well written.
4. IO ports and IO memory under Linux
Since I don't know much about the IO memory allocation of IMX257, I have asked many so-called technicians in Zhou Ligong's company. Everyone's answer is, "Hello, the information we provide is just the CD information on the official website." Every time I encounter this kind of words, I am really speechless. I can only say, well, I can only say that my IQ is too low and the information is too profound.
Okay, I won’t waste any more time. This time, I will explain the allocation of io ports based on the gpio driver of S3C24XX.
1. Define some registers (data registers, configuration registers)
In the figure above, gpio_va is the base address of our GPIO, which is used to determine the pointer address of the GPFCON configuration register and the GPFDACT data register.
, its initialization will be explained in the init function.
2. Determine the gpio base address in the init function
The premise for the normal function of our registers is the base address gpio_va, so we allocate IO ports in the init function
As shown in the figure, the IO port with the address 0x56 of gpio is assigned as gpio_va, which also determines the previous configuration register and data register.
3. Configure GPIO in the open function
Just like the MCU program, you need to configure GPIO before using it. The principle is the same:
Assign initial values to the GPIO pins. In the future, you can directly use this register to assign values to the GPIO pins.
4. Release the IO port in the exit function
After using the GPIO pin, we need to release the IO port we applied for.
To sum up, it is very simple, apply, configure, and release
Apply for IO port à Configure GPIO port as output (input) à Assign value to data register (read) à Release IO port
Attached driver:
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 12 #define DEVICE_NAME "leds" 13 /* After loading mode, execute the "cat /proc/devices" command to see the device name*/ 14 #define LED_MAJOR 231 /* Main device number*/ 15 16 static struct class *leds_class; 17 static struct class_device *leds_class_devs[4]; 18 19 /* bit0<=>D10, 0: on, 1: off 20 * bit1<=>D11, 0: on, 1: off 21 * bit2<=>D12, 0: on, 1: off twenty two */ 23 static char leds_status = 0x0; 24 static DECLARE_MUTEX(leds_lock); // define and assign values 25 26 //static int minor; 27 static unsigned long gpio_va; //gpio base address 28 #define GPIO_OFT(x) ((x) - 0x56) 29 //GPIO configuration register 30 #define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56050))) 31 //GPIO data register 32 #define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56054))) 33 /* When the application executes open(...) on the device file /dev/leds, 34 * will call the s3c24xx_leds_open function 35 */ 36 static int s3c24xx_leds_open(struct inode *inode, struct file *file) 37 { 38 int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); 39 switch(minor) 40 { 41 case 0: /* /dev/leds */ 42 { 43 //Configure pin 3 as output 44 //s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); 45 GPFCON &= ~(0x3<<(4*2)); 46 GPFCON = (1<<(4*2)); 47 48 //s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); 49 GPFCON &= ~(0x3<<(5*2)); 50 GPFCON = (1<<(5*2)); 51 52 //s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); 53 GPFCON &= ~(0x3<<(6*2)); 54 GPFCON = (1<<(6*2)); 55 56 // All output 0 57 //s3c2410_gpio_setpin(S3C2410_GPF4, 0); 58 GPFDAT &= ~(1<<4); 59 60 //s3c2410_gpio_setpin(S3C2410_GPF5, 0); 61 GPFDAT &= ~(1<<5); 62 //s3c2410_gpio_setpin(S3C2410_GPF6, 0); 63 GPFDAT &= ~(1<<6); 64 65 down(&leds_lock); 66 leds_status = 0x0; 67 up(&leds_lock); 68 69 break; 70 } 71 72 case 1: /* /dev/led1 */ 73 { 74 s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); 75 s3c2410_gpio_setpin(S3C2410_GPF4, 0); 76 77 down(&leds_lock); 78 leds_status &= ~(1<<0); 79 up(&leds_lock); 80 81 break; 82 } 83 84 case 2: /* /dev/led2 */ 85 { 86 s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); 87 s3c2410_gpio_setpin(S3C2410_GPF5, 0); 88 leds_status &= ~(1<<1); 89 break; 90 } 91 92 case 3: /* /dev/led3 */ 93 { 94 s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); 95 s3c2410_gpio_setpin(S3C2410_GPF6, 0); 96 97 down(&leds_lock); 98 leds_status &= ~(1<<2); 99 up(&leds_lock); 100 101 break; 102 } 103 } 104 return 0; 105 } 106 static int s3c24xx_leds_read(struct file *filp, char __user *buff, 107 size_t count, loff_t *offp) 108 { 109 int minor = MINOR(filp->f_dentry->d_inode->i_rdev); 110 char val; 112 switch (minor) 113 { 114 case 0: /* /dev/leds */ 115 { 116 copy_to_user(buff, (const void *)&leds_status, 1); 117 break; 118 } 119 case 1: /* /dev/led1 */ 120 { 121 down(&leds_lock); 122 val = leds_status & 0x1; 123 up(&leds_lock); 124 copy_to_user(buff, (const void *)&val, 1); 125 break; 126 } 127 case 2: /* /dev/led2 */ 128 { 129 down(&leds_lock); 130 val = (leds_status>>1) & 0x1; 131 up(&leds_lock); 132 copy_to_user(buff, (const void *)&val, 1); 133 break; 134 } 135 case 3: /* /dev/led3 */ 136 { 137 down(&leds_lock); 138 val = (leds_status>>2) & 0x1; 139 up(&leds_lock); 140 copy_to_user(buff, (const void *)&val, 1); 141 break; 142 } 143 } 144 return 1; 145 } 146 static ssize_t s3c24xx_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 147 { 148 //int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); 149 int minor = MINOR(file->f_dentry->d_inode->i_rdev); 150 char val; 151 copy_from_user(&val, buf, 1); 152 switch (minor) 153 { 154 case 0: /* /dev/leds */ 155 { 156 s3c2410_gpio_setpin(S3C2410_GPF4, (val & 0x1)); 157 s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1)); 158 s3c2410_gpio_setpin(S3C2410_GPF6, (val & 0x1)); 159 down(&leds_lock); 160 leds_status = val; 161 up(&leds_lock); 162 break; 163 } 164 case 1: /* /dev/led1 */ 165 { 166 s3c2410_gpio_setpin(S3C2410_GPF4, val); 167 if (val == 0) 168 { 169 down(&leds_lock); 170 leds_status &= ~(1<<0); 171 up(&leds_lock); 172 } 173 else 174 { 175 down(&leds_lock); 176 leds_status = (1<<0); 177 up(&leds_lock); 178 } 179 break; 180 } 181 case 2: /* /dev/led2 */ 182 { 183 s3c2410_gpio_setpin(S3C2410_GPF5, val); 184 if (val == 0) 185 { 186 down(&leds_lock); 187 leds_status &= ~(1<<1); 188 up(&leds_lock); 189 } 190 else 191 { 192 down(&leds_lock); 193 leds_status = (1<<1); 194 up(&leds_lock); 195 } 196 break; 197 } 198 case 3: /* /dev/led3 */ 199 { 200 s3c2410_gpio_setpin(S3C2410_GPF6, val); 201 if (val == 0) 202 { 203 down(&leds_lock); 204 leds_status &= ~(1<<2); 205 up(&leds_lock); 206 } 207 else 208 { 209 down(&leds_lock); 210 leds_status = (1<<2); 211 up(&leds_lock); 212 } 213 break; 214 } 215 } 216 return 1; 217 } 218 /* This structure is the core of the character device driver 219 * The open, read, write and other functions called by the application when operating the device file. 220 * The corresponding function specified in this structure will eventually be called 221 */ static struct file_operations s3c24xx_leds_fops = { 223 .owner = THIS_MODULE, /* This is a macro that pushes to the __this_module variable that is automatically created when compiling the module*/ 224 .open = s3c24xx_leds_open, 225 .read = s3c24xx_leds_read, 226 .write = s3c24xx_leds_write, 227 }; 228 229 /* 230 * This function will be called when the insmod command is executed 231 */ 232 static int __init s3c24xx_leds_init(void) 233 { 234 int ret; 235 int minor = 0; 236 237 gpio_va = ioremap(0x56, 0x100); 238 if (!gpio_va) { 239 return -EIO; 240 } 241 /* Register character device 242 * The parameters are the primary device number, device name, and file_operations structure; 243 * In this way, the major device number is associated with the specific file_operations structure. 244 * When operating the device file whose main device is LED_MAJOR, the relevant member function in s3c24xx_leds_fops will be called 245 * LED_MAJOR can be set to 0, indicating that the kernel automatically assigns the major device number 246 */ 247 ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops); 248 if (ret < 0) { 249 printk(DEVICE_NAME " can't register major number\n"); 250 return ret; 251 } 252 leds_class = class_create(THIS_MODULE, "leds"); 253 if (IS_ERR(leds_class)) 254 return PTR_ERR(leds_class); 255 256 leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, 0), NULL, "leds"); 257 258 for (minor = 1; minor < 4; minor++) 259 { 260 leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor); 261 if (unlikely(IS_ERR(leds_class_devs[minor]))) 262 return PTR_ERR(leds_class_devs[minor]); 263 } 264 265 printk(DEVICE_NAME "initialized\n"); 266 return 0; 267 } 268 269 /* 270 * This function will be called when the rmmod command is executed 271 */ 272 static void __exit s3c24xx_leds_exit(void) 273 { 274 int minor; 275 /* Uninstall driver */ 276 unregister_chrdev(LED_MAJOR, DEVICE_NAME); 277 278 for (minor = 0; minor < 4; minor++) 279 { 280 class_device_unregister(leds_class_devs[minor]); 281 } 282 class_destroy(leds_class); 283 //Release IO port 284 iounmap(gpio_va); 285 } 286 287 /* These two lines specify the driver's initialization function and uninstallation function*/ 288 module_init(s3c24xx_leds_init); 289 module_exit(s3c24xx_leds_exit); 290 291 /* Describe some information about the driver, not required*/ 292 MODULE_LICENSE("GPL");
Previous article:Detailed explanation of STM32 memory map
Next article:STM32 USART common serial port application, debugging summary
- Popular Resources
- Popular amplifiers
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- Sn-doped CuO nanostructure-based ethanol gas sensor for real-time drunk driving detection in vehicles
- Design considerations for automotive battery wiring harness
- Do you know all the various motors commonly used in automotive electronics?
- The first article is about downloading GD32L233C development resources
- In a superheterodyne receiver, after selecting the frequency of the signal, how does the receiver know what the frequency is in Hz?
- Review summary: Free review: AI development board based on Allwinner R329 chip
- Computer Organization and Design: Hardware-Software Interface (RISC-V Edition)
- What are the advantages of using stepper servo motors to replace stepper motors?
- FPGA_100 Days_Calculator Design
- What is a thick film non-inductive resistor? What is its main use?
- The video signal carrying capacity close to 1MHz is not enough. What op amp can I use to design a voltage follower?
- MCU's ADC sampling error
- LC Application Circuit Collection