0
\$\begingroup\$

A couple of months ago I started working on developing my own computer to learn more about its low-level functionality, the design process and the architecture behind it. Although I look at real-world examples of other computers I try not to just copy, but use those as inspiration to develop the architecture of my computer myself.

Besides 555 CMOS timers, some EEPROMs and HM62256 SRAM everything else is supposed to be built discretely with 74 series ICs.

I planned it to have a 16 bit word length, but didn't think about the implications this would have for the RAM as opposed to all the 8 bit computer examples I've read about.

My first approach was to make the RAM word-addressable, so I hooked up the address lines of two HM62256s and effectively had 16 bits stored in every address. This would be easy to output to the bus and work with, so I developed an instruction set around it, with a 5 bit opcode and the remaining 11 bits depending on the type of the instruction left for registers or immediate values.

While doing more and more research however I realized that word-addressable RAM doesn't seem to be common in real world computers. They all seem to be addressable by byte. This wouldn't be a problem for 8-bit computers, but if I plan on fetching my instructions in just one cycle doing it this way would be a problem.

Especially looking at ASCII it seems important to have a way to store single bytes in RAM, not just whole words. If I were to store ASCII characters in 2-byte RAM addresses, there'd be 50% wasted storage. I could just ignore this and carry on with my plans, but I'm also really intrigued by how this is solved.

How am I able to fetch multiple bytes from a byte-addressable RAM simultaneously to use them in my instruction register? Would I have to read two bytes in sequence and re-assemble them into a single 16-bit instruction before storing it in the register? What about other kind of data, does reading a 2-byte value require me to load two bytes from RAM manually into registers and re-assemble them?
Is there a more useful (or simpler) way I just can't think of?

Sorry for the long read for that short question, but thanks for any replies.

\$\endgroup\$
5
  • \$\begingroup\$ The 8086 (not 8088) added a bus high enable line. When \$BHA=0\$ and \$A_0=0\$ then the implied data bus was \$D_0-D_{15}\$; when \$BHA=0\$ and \$A_0=1\$ then the implied the data bus as \$D_8-D_{15}\$; when \$BHA=1\$ and \$A_0=0\$ then the implied data bus was \$D_0-D_{7}\$. The remaining bus cycle case was supposed to be ignored. \$\endgroup\$
    – jonk
    Commented Sep 11, 2017 at 23:37
  • 2
    \$\begingroup\$ you treat word access as you do now but handle single byte and non-aligned words as special cases. Before that you need switch word-address to byte-address(by shifting the address bus up one bit on the momory side and wire the LSB on the memory side to 0). Currently most RISC-like ISAs choose to implement non-aligned word access and byte access as special instructions and let compiler opt-in, to reduce hardware complexity. \$\endgroup\$ Commented Sep 11, 2017 at 23:39
  • 1
    \$\begingroup\$ As others have said, you leave the RAM as word-addressable and then select which byte out of the word you want. \$\endgroup\$
    – pjc50
    Commented Sep 12, 2017 at 9:03
  • \$\begingroup\$ Since addresses for data are supposed to be reserved at the end of programs instructions should always be aligned and at most one byte of data at the end is unused, so unaligned word access really only makes sense in few cases. If I were to access bits 0..7 only, does it make sense to shift them over to be put on the bus at 8..15, or do I leave them essentially shifted to the left by 8 bits? \$\endgroup\$
    – padarom
    Commented Sep 12, 2017 at 9:11
  • \$\begingroup\$ And if I understand it correctly for unaligned word access I'd need to essentially address MAR+1 in the first chip and MAR in the second and put them on the bus in reverse order? \$\endgroup\$
    – padarom
    Commented Sep 12, 2017 at 9:15

2 Answers 2

1
\$\begingroup\$

Most architectures that encounter this situation have two (or more) different instructions for accessing Bytes or Words. If you have a 16bit datapath (word size), that means the hardware outside the memory can accept/deliver 2 bytes in a single cycle. How the memory handles this is up to the memory. If you only have an 8-bit databus within the memory unit, then you will have to do one of the techniques you suggested (2 cycles, 2 register transfers, etc).

However, I've never seen that in modern hardware because it's silly when cost and technology do not practically require it. As a historical note:

The very first PC processor - the 8088 from intel - was a 16-bit processor with an 8-bit memory bus. And there were 386's [32-bit data bus] with 16-bit [external memory] bus (386SX). -- TurboJ

Now, almost all practical hardware has memory (L1) width = processor width. The instructions will simply pick what to do with the mapping. Load word instruction = 1:1. Load byte = 2:1 (usually there is a range of instructions that determine what to do with the other byte in the datapath that isn't being loaded from memory).

\$\endgroup\$
2
  • \$\begingroup\$ The very first PC processor - the 8088 from intel - was a 16-bit processor with an 8-bit memory bus. And there were 386's with 16-bit bus (386SX). \$\endgroup\$
    – Turbo J
    Commented Sep 12, 2017 at 3:21
  • \$\begingroup\$ @TurboJ -- Oh yeah! The good old days! I meant recently, let's include the historical note. \$\endgroup\$ Commented Sep 12, 2017 at 4:51
1
\$\begingroup\$

How am I able to fetch multiple bytes from a byte-addressable RAM simultaneously to use them in my instruction register? Would I have to read two bytes in sequence and re-assemble them into a single 16-bit instruction before storing it in the register? What about other kind of data, does reading a 2-byte value require me to load two bytes from RAM manually into registers and re-assemble them? Is there a more useful (or simpler) way I just can't think of?

Yes, there is. Most modern general-purpose computers are both (a) byte-addressable and at the same time (b) load every byte of an multi-byte instruction simultaneously into the instruction register (IR) in a single memory cycle.

As you know, the system bus between the CPU and the RAM is divided into the address bus, the data bus, and the control bus. (Sometimes I/O devices are also attached to this bus). The difference between byte addressing and word addressing is in how address registers are connected to the address bus; it has nothing to do with the width of the data bus.

Many processors (such as modern ARM Thumb and ARM Thumb 2) have mostly 16-bit-wide instructions. (Some people say 16-bits is the "sweet spot" compared to processors with 8-bit instructions or processors where every instruction is at least 32 bits wide).

The simplest approach for 16-bit instructions is to make the data bus 16 bits wide, and load the entire instruction from RAM into the instruction register (IR) in a single memory cycle. Many such CPUs have the least-significant bit of the program counter (PC) hard-wired to zero, forcing every instruction to be "aligned" to a 16-bit boundary.

There are at least 3 popular ways of handling 8-bit data (such as ASCII and UTF-8) with such a 16-bit data bus (i.e., different ways of dealing with A0 of the address register), without wasting storage:

  • (a) Simplest RAM hardware, simplest CPU hardware, complex software: Each address pin and each control lines /WE, /OE, and /CE on either of the 2 RAM chips is directly wired to the corresponding pin of the other RAM chip. This forces the RAM to always read and write a full 16 bits at a time, like word-addressed systems; there is no STORE BYTE instruction. To overwrite a single ASCII letter pointed to by a pointer P in the middle of a string, software generally requires at least 3 instructions: LOAD to read the entire 16 bit word containing that byte into some register, update the appropriate byte of that register (depending on the least-significant bit A0 of P), and then STORE the entire 16-bit word back out with the updated byte value.
  • (b) Simplest RAM hardware, complex CPU hardware, simplest software: We use the exactly the same RAM setup as (a), and the software appears to use the same "STORE byte to P" instruction as (c), but when the CPU executes that instruction, it takes (at least) 2 memory cycles to read the entire 16 bit word containing that byte (possibly into some microarchitectural register not really visible in the programmer model), update the appropriate byte of that register, and then write the entire 16-bit register back out.
  • (c) less-simple RAM hardware, less-simple CPU hardware, simplest software: the CPU has a "STORE byte to P" instruction. During such a "store byte" instruction, one RAM chip sees its /WE line yanked Lo, i.e., active, the other RAM chip is left unchanged, with its /WE and /OE left Hi (inactive). The CPU generates a separate /WE_0 and /WE_1 byte-lane write signals dependent on the pointer P's least-significant bit (A0): /WE_0 is activated during a STORE BYTE when A0 is 0; /WE_1 is activated during a STORE BYTE when A1 is 1; and both /WE_0 and /WE_1 are activated during a STORE 16-bits instruction.

Approach (c) is common enough that many "x16 bit" chips (memory chips with 16 data I/O pins per chip), such as the ISSI IS61WV3216BLL, have 2 separate Byte Lane Select pins. Approach (c) is common enough that 8-byte-wide (64 bit wide) DIMM memory modules have a control bus with 8 separate byte lane strobe signals (to support STORE Byte, STORE 16-bits, STORE 32-bits, etc.).

Approach (b) was once common in microcoded minicomputers, but it has fallen out of favor because it makes pipelining much more difficult for no real benefit.

Some teams develop a "high-end" computer with hardware that directly supports (c) or (b) with fast "STORE byte" instructions, and also a compatible "low-end" computer with simpler hardware more like (a). Most instructions operate exactly the same on these two machines, but when the low-end machine tries to execute the "STORE byte" instruction, it's trapped as an unimplemented instruction, and the unimplemented instruction exception handler simulates that instruction using a series of instructions that are implemented, much like (a).

Occasionally programmers need to compactly store data that is not byte-aligned (such as packed BCD digits, bit-oriented variable-length codes such as Huffman codes, etc.), and they are forced to write code that operates much like (a).

Historical note

Early RAM chips had only 1 data I/O pin per chip. Back then, 16-bit-wide data buses required a minimum of 16 separate RAM chips. The cost of the chips and the PCB space to hold all those chips made that approach much more expensive than the same "amount" of memory using 8-bit-wide buses. This is why some popular computers used the 68008 or the 8088, which had an 8-bit bus that did exactly as you suggested -- 16-bit instructions and 16-bit data values were loaded from RAM 8 bits at a time and re-assembled before storing into the instruction register (IR) or some data register.

Fortunately, today it's fairly easy to build a 16-bit-wide buses with "x8 bit" chips (memory chips with 8 data I/O pins per chip), such as 2 of the Hitachi HM62256A or 2 of the Alliance AS6C1008. A byte-addressable CPU connected to such a memory array would not connect the least-significant bit of the CPU address registers (A0) to the least-significant address pin of the memory chips (A0) -- that would make things word-addressable. Instead, to support byte-addressability, A1 of each address registers and address bus would be connected to A0 of both memory chips; A2 of the data registers and address bus would be connected to A1 of both memory chips, etc.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.