Search code examples
memorymicrocontrollerrammicroprocessorsrom

About memory and how to calculate the ending address


I read about base address and memory map of controllers, I have a doubt that whether all the memory locations are of 1byte only and also that suppose that if the memory map says that the base address is 0x0000 for ROM and its size is 8K ( I believe it is 8 Kilo-bytes = 8 * 1024 bytes, in short, is 8K) how can I calculate the ending address of ROM ie. 0x1FFF. And can I conclude that after seeing the base address as 0x0000 for ROM, it is accessed by 32-bit processor, and the ROM can store 4GB?

Is my understanding correct? 0x0000 = 0000_0000_0000_0000 means 16 address lines from micro-processor points to the location 0000_0000_0000_0000 in ROM, in that address in ROM, the ROM can hold 8 bit of data.

In ARM-Cortex M3, There are 32-bit address lines and 32-bit data lines, and the 32-bit registers in a processor. So since there are 32-bit address lines, we can say that we have 4,294,967,296 combinations of 1' and 0's representing 4,294,967,296 memory locations, with each location holding capable of 1-byte data holding capacity. My doubt is since a register is 32-bit here is it like having 4 continuous memory locations (with 4 different addresses) for a register as memory is 1-byte.

I think these questions seem to be trivial in this great platform, but it would be a little helpful it is explained in simple terms or and links are suggested.:)


Solution

  • With possibly rare exceptions, current products address memory in units of bytes. so an "address" is a "byte address". Now saying that as you dig into the logic, there are MANY cases where buses will have non-byte addressing. For example if an sram is 32 bits wide, leading up to that sram you have a normal byte based address, there is no reason generally to preserve the lower two address bits and waste signals on them they are not used when addressing what is essentially four bytes at one time. Same goes for when you access say a 16 or 32 bit control register (let's say a uart baud rate register in a uart peripheral) eventually the lower bits of the address are stripped off as unused DEEP IN THE LOGIC.

    It is correct to think that when a 16 bit transfer happens be it to actual ram or rom/flash or a control register, that is two bytes. and from a BYTE ADDRESS perspective those two bytes might be 0x40001000 and 0x40001001, but, as a programmer and also from the system level view that 16 bit transfer is not a two byte transfer at those two addresses but a 16 bit transfer at the one address 0x40001000 (big or little endian, you mentioned cortex-m so basically little endian in your case).

    While there are exceptions, the processors you are likely to use have busses that are wider than a byte, the cortex-ms are likely 32 bit, maybe the bigger ones 64 bit, but definitely not 8 bit. So every transfer you do be it one byte, a halfword, word or double-word (64 bit) are done across that bus and you don't magically remove wires/signals in the chip when you do that they are always there. Reads generally (there are no doubt exceptions) are done in units of full bus width, so if you were to read a single byte from memory, you will generally find that the system is designed such that the whole 32 bits or 64 bits that contain that byte are read and the processor isolates the one byte returned from the bus and discards the others. For a write the one byte is placed on the bus per the bus rules (varies by processor) and the other data lines on the bus are often driven with garbage or stale data from the bus controller or sometimes left to float. Per the bus rules/design an indication to the other side as well as stating this is a write will indicate the size either through a byte mask (one bit for each byte lane on the bus one state (one or zero) indicating the byte is valid/used and the other state indicating it is not) or a byte based length in combination with address, etc. ARM has tended to be byte mask based, and you can read up on that.

    Understand that ARM makes processor IP it does not make chips, ARM has almost nothing to do with what the chip vendor hooks up where, there is no rule whatsoever that says that address 0x00000000 is rom/flash. Historically the entire address space (how big that is is determined by the core purchased, ideally 32 bit but not always that way for arm read the documentation for the core on arms web site) was fair game to the chip designers. Access to the core control registers was done through special coprocessor instructions. With the Cortex-m's ARM did dictate wide address ranges, if you want a rom you should put it in this range, this range should be sram and above this address is internal the access to that range will not go out on the bus it will be consumed inside the core. As a chip designer though you are still free to do what you want with what comes out understanding how the logic in the core uses those address spaces (if there is a cache then there might not be a full blow mcu so you want to put control registers for your peripherals in the range arm recommends to avoid caching of that address space).

    Because the processor (NOT CHIP) boots by examining words at the first few word locations starting at address 0x00000000, the chip designer needs to provide a way for the software developer to put their vector table in front of that so that their program runs. Some chip designers simply design in the address for the user flash at that address with respect to the ARM. Others like the stm32's for example some/many TI cortex-m based chips, use a different address, the stm32s for example all/most use 0x08000000, so FULL access to the flash is at that address, BUT during boot with the chip set with certain settings, the chip logic will take accesses to a small percentage of the 0x00000000 based addresses and access the user flash. So for development on an stm32 family you would build your program for 0x08000000 not 0x00000000 to take advantage of the whole flash. (the stm32s can be booted from ram 0x20000000 and from an internal bootloader on a separate flash and the 0x00000000 address space is routed to those resources depending on the boot mode).

    So with all of that, 32 bit registers is not usually relevant, but in this case it lines up, yes 32 bit registers, and at least from an instruction set perspective there is a 32 bit address space which means 0x100000000 possible unique addresses, byte based. Doesn't mean you can access every one of those bytes without crashing in a specific chip implementation, but from an instruction set perspective yes, 32 bit address so 0x10000000 possible addresses.

    The general purpose registers, r0,r1,r2,r3. Are 32 bits in size, they are not memory mapped you can't address them. There are instructions in the instruction set that allow for 8,16,32,64 (and larger) accesses to the address space (ldrb,ldrh,ldr,ldrd, strb,strh,str,strd, ldm/stm). And as mentioned above how an 8 bit or 16 bit or 32 or 64 bit access happens on the bus is part of the cores system design a 64 bit write to address 0x1004 will very likely become two separate bus transfers one 32 bit one at 0x1004 and one 32 bit transfer at 0x1008. But a 64 bit write to address 0x1000 will very likely be one bus transfer with either one clock or two worth of data (plus a handful of clocks of overhead for the transfer) depending on the bus width 32 or 64 bits. From an instruction set perspective that is all hidden, it is just an strd instruction. if performance is a goal then you want to be aware of the side effects of the core/chip you are using if not then just use strd per the instruction rules.

    If a chip is said to have 8KBytes of flash in its documentation then yes that means 0x8 * 0x400 = 0x2000 so there are 0x2000 individual bytes that can be accessed in that flash in the address space of BASE+0x0000 to BASE+0x2000-1 or BASE+0x0000 to BASE+0x1FFF. If BASE is 0x00000000 then 0x0000 to 0x1FFF, that is correct. Depending on the chip design, etc, some will roll over some won't so if you had a bug and accessed 0x2000 it might crash it might return garbage or a fixed patter or it might be masked 0x2000 & 0x1FFF = 0x0000 and the item at address 0x0000 will be returned. I would not try nor rely on that to work unless you work at the chip company and have regular communication with the chip engineers and have a damn good reason for doing it (detecting the size of the flash for example).

    That does not mean you could/should do a 16 or 32 bit transfer at the address 0x1FFF, if you are doing a byte (8 bit in the case of arm cortex-m) transfer then 0x1FFF is a valid address (in general) for an 8KByte flash. But 0x1FFE is the last address for a 16 bit transfer and 0x1FFC the last address for a 32 bit transfer.

    Side note: memory in general when you are working as a chip engineer or board designer is in units of bits. So when the chip designers go to purchase or build an 8KByte flash, they are searching for a 64K flash as memories are defined by bits when you buy them (not memory sticks you buy at Amazon for your PC, but chips or IP). If you have a 16Gigabyte memory module in your computer and that module has 64 data signals and 4 chips that means 4Gbytes per chip. But when you look up the part number for that chip it is a 32G chip, without a bit/byte nomenclature, simply 32G chip. Just fuel for the fire on how confusing these things get depending on where you go professionally with your career. Even with decades of experience this one fact trips folks up on a regular basis, the pure hardware folks are using the bit size when ordering parts or IP, the software folks the byte size and the folks in between struggling to translate between the teams. If the two teams interact there is often confusion. (Hardware/chip folks naturally know what is going on but software folks tend to focus purely on byte based addressing and don't often naturally flow between bit, byte, halfword, word, etc based addressing).