Tuesday, April 1, 2025

Entry 20: 4-bit 2-to-1 multiplexer

In the previous entry, we designed this:

Adding DIP switches

Now, I also want to be able hook up 4 DIP switches to MAR and 4 DIP switches to MDR so that I can program the memory. Lets begin with DIP's for the address. 

So, lets ask Grok. "I have a four lines from MAR Q to RAM A. But I also want to hook up 4 DIP switches to RAM A. Is there a chip that will take two sets of inputs, each with four lines, and output one set of four lines? It should have pin where I can select if the input should be from MAR or from the DIP switches."

Yes, you can absolutely connect both your MAR (Memory Address Register) and the 4 DIP switches to the RAM and select between them using a single chip! What you’re looking for is a 4-bit 2-to-1 multiplexer (often called a 2:1 MUX). This chip takes two sets of 4-bit inputs and outputs one set of 4 bits, based on a select pin that chooses between the two sources—in your case, the MAR or the DIP switches. There’s a perfect IC for this: the 74LS157 (Quad 2-to-1 Multiplexer), which handles exactly 4 bits with a single select line.  It works perfectly with your setup (16x4 SRAM, 4-bit address/data buses). Let’s dive into how to use it for your setup.

Why a Multiplexer?

  • Problem: You want RAM’s A0–A3 inputs to come from either the MAR's Q0-Q3 or 4 DIP switches, but not both at once.
  • Solution: A 2-to-1 multiplexer acts like a switch, choosing one of two 4-bit inputs to pass to its output, controlled by a select pin. The output then feeds RAM’s A0–A3.

Why This Works

  • Setup: MAR latches the address bus directly, and the multiplexer selects between MAR Q0–Q3 and DIP switches to drive RAM A0–A3. This lets you:
    • Use MAR to hold an address from the bus, then decide if RAM uses that or a manual DIP switch value.
    • Bypass MAR entirely with DIP switches when needed.

Multiplexor

"How can I build a 2-to-1 multiplexer using basic gates?"

Building a 2-to-1 multiplexer (MUX) using basic gates is a great way to understand its inner workings. A 2-to-1 MUX selects between two inputs (A and B) and outputs one based on a select signal (S), which is exactly what you need to switch between the MAR’s Q0–Q3 and your DIP switches to drive the RAM’s address pins. For your 4-bit system, you’ll need four 1-bit 2-to-1 MUXes, but let’s start by designing a single 1-bit version using basic gates (AND, OR, NOT), then scale it to 4 bits. Here’s how to do it.


2-to-1 Multiplexer Basics

  • Inputs:
    • A: First input (e.g., MAR Q0).
    • B: Second input (e.g., DIP switch).
    • S: Select line (0 = A, 1 = B).
  • Output: Y (either A or B, based on S).
  • Logic:
    • If S = 0, Y = A.
    • If S = 1, Y = B.


Building a 1-Bit 2-to-1 MUX with Gates

You can construct this using:

  • 2 AND gates (e.g., 74LS08).
  • 1 OR gate (e.g., 74LS32).
  • 1 NOT gate (inverter, e.g., 74LS04).

Step-by-Step Design (replaced with picture)


Scaling to 4 Bits

Since your MAR and RAM use 4-bit addresses (A0–A3), you’ll need four 1-bit MUXes, one for each bit (A0, A1, A2, A3). All four share the same select signal (S), so you only need one NOT gate for S̅.

ACEL

I designed a 1 bit 2-to-1 MUX according to the design above. It is called TwoToOneMux.

  • The input pins are called A,B
  • The select pin is called S
  • The output pin is called Y

I also designed a quad 1 bit 2-to-1 MUX (like the 74LS157). It is called QuadTwoToOneMux.

  • The input pins are called A[4], B[4]
  • The select pin is called S (common to all)
  • The output pins is called Y[4]

The final setup with RAM, MAR, MDR, address bus, data bus, 2 MUX, DIP's for address and data, and control buttons is next, .. in entry 21.

Entry 19: Memory Address Register (MAR) and Memory Data Register (MDR)

So, in entry 18 we designed SixteenByFourRam:

This chip is "asynchronous", it has no clock. Therefore, it does not seem right to connect the data bus directly to D and the address directly to A - we cannot be certain that the busses hold the values we need when we need them. 

I investigated this issue further, and it seems that a Memory Address Register (MAR) and a Memory Data Register (MDR) are used to address this particular problem.

Memory Address Register (MAR) and a Memory Data Register (MDR)

So I asked Grok "I am thinking about adding a Memory Address Register and a Memory Data Register to my setup. Can you tell me what these are?":

Adding a Memory Address Register (MAR) and a Memory Data Register (MDR) to your setup is a brilliant next step! These are critical components in computer architecture, including microprocessors like the 8085, and they’ll enhance your 16x4 SRAM system by providing dedicated storage for addresses and data during memory operations. Let’s break down what they are, how they work, and how to integrate them into your current asynchronous design with no clock signal (using D latches).


What Are MAR and MDR?

1. Memory Address Register (MAR)

  • Purpose: Holds the address of the memory location that the RAM wants to access (read from or write to).
  • Function: Acts as a buffer between the address bus and the memory. When you want to access a specific location (e.g., 0010 in your 16x4 RAM), the MAR stores that address temporarily so the memory can use it to select the right row.
  • Size: Matches the address bus width. In your setup, it’s 4 bits (A0–A3) to handle 16 locations.

2. Memory Data Register (MDR)

  • Purpose: Holds the data being read from or written to memory. It serves as a temporary storage for data moving between the CPU/registers and memory.
  • Function: Buffers data on the data bus. When writing, it holds the data to be stored; when reading, it captures the data from memory before it’s used elsewhere (e.g., loaded into a register).
  • Size: Matches the data bus width. In your setup, it’s 4 bits (D0–D3).

Why Add MAR and MDR?

  • Separation of Concerns: MAR isolates address handling from data handling, making your system more organized and scalable, like a real CPU.
  • Improved Control: They allow you to stage memory operations—set the address, then handle the data—reducing bus contention and simplifying timing.
  • Simulation of Real Systems: MAR and MDR are standard in architectures, so adding them brings your setup closer to a modern CPU.

So, the MAR and the MDR are four bit registers, and we can use "FourBitRegister" (see entry 15):

Summary

This is how I understand the connections between RAM, MDR, MAR, and the busses:

  • An address on the address bus can be loaded into MAR, through the D pins, by setting Load = 1. MAR then loads the data on the rising edge of the clock.
  • Data on the data bus can be loaded into MDR, through its D pins, by setting its Load = 1 (also on the rising edge).
  • RAM continuously takes its address from MAR and its data from MDR (no clock).
  • When RAM Load = 1, data on D is stored at address D.
  • When RAM Write = 1, RAM writes the content att address A onto the data bus as well as the address bus. 
  • MAR and MDR can also write to the address bus / data bus by setting its Write = 1.

ACEL connections:

  //Define chips
  auto* ram = new SixteenByFourRam((char*)("SRAM"));
  auto* mdr = new FourBitRegister((char*)("MDR"));
  auto* mar = new FourBitRegister((char*)("MAR"));
  auto* dataBus = new BusLineFour((char*)("Data Bus"));
  auto* addressBus = new BusLineFour((char*)("Address Bus"));


  //Add all chips to be emulated here
  Chip* chips[] = {
    ram, mdr, mar, dataBus, addressBus
  };

  //Add all connections here
  ConnectionBase* connections[] = {
    new ConnectionFour(addressBus->W, mar->D),
    new ConnectionFour(dataBus->W, mdr->D),
    new ConnectionFour(mar->Q, ram->A),
    new ConnectionFour(mdr->Q, ram->D),
    new ConnectionFour(ram->W, addressBus->X),
    new ConnectionFour(ram->W, dataBus->X),
    new ConnectionFour(mar->W, addressBus->X),
    new ConnectionFour(mdr->W, dataBus->X),
  };

We also need to connect the control pins. But first, I need to figure out where to put DIP switches in all of this so that we can program the RAM... in entry 20.