-
Pointers
04/27/2017 at 19:26 • 2 commentsI realized it's much easier to do the pointer register than I thought. The logic is just:
If destination register = Pointer-Data-Address then
destination bus = Pointer-Address
else
destination bus = destination register
Or in VHDL:
dstAddr <= ptrReg when dstRegOut=PTR_DATA_ADDR else dstRegOut;
So it won't cost me a clock cycle to execute this function which is great because modifying the constant 2-clock execute cycle would be a pain. All instructions executing in 2 clock cycles is elegant and I'd prefer to keep it that way.
-
Address decoding
04/27/2017 at 18:02 • 3 commentsI assumed address decoding will use EEPROMs. I mean, I will need to decode the 8-bit source and destination addresses into read and write lines. I hope I could find a 256x16 or 256x32 EEPROM, but this is by far too small for these days. I have found the AT28C64B EEPROM, but it is 8K x 8. That's 12 address bits (4 extra). And only 8 bits out. And it's an active device. Unfortunately Atmel doesn't have a 16-bit output EEPROM. I'm not sure how many of these I will need until I'm mostly done with the design, but it's my current plan.
I'd prefer not to drop to a PLD or PLA. It feels a little like cheating even though these chips existed in the 80s. Not that there is a big difference in these vs. an EEPROM for address decoding. I'll think on it more.
It's just so easy in an FPGA: loadAluA <= '0' when dstAddr=x"3F" else '1';
-
Usefulness
04/26/2017 at 00:38 • 0 commentsIn looking into my Altair 8800 front panel design, I checked out a lot of old computers around that time. In my opinion, the big breakthrough in making computers really useful came with Apple. The big difference was that instead of using a separate terminal with a serial interface, the keyboard and monitor were built-in as part of the computer. The keyboard was fairly simple - you pushed a key and it put an 8-bit value on the bus. Really it was just an off-the-shelf 6502 CPU that lots of other computers were using, but the video circuit was where the genius engineering was. That's what made the difference between useful and not-so-useful. And it had a built-in bootloader, so turn it on and start programming.
I'd compare a lot of the CPU projects on here to the Altair 8800 - a solid CPU design, but their peripheral interfaces need to be expanded. Loading a program with DIP switches, or pulling off an EEPROM to program separately is kind of a pain. And looking at an output on LEDs or a 2x16 LCD dot-matrix is neat, but makes it really tough to develop on the computer itself. Sure a video interface is tough to do with TTL chips, but if it was easy it wouldn't be fun.
I'd like my computer to be at least as useful as the original Apple computer. But I'd prefer not to do an obsolete video interface like composite or even VGA. If possible I may take a stab at DVI or HDMI. That's pretty advanced for a TTL type computer, but there may be ways to simplify. HDMI has three serial data channels (one for each color) plus a clock channel. And they run FAST. However, I think it's outside the scope of this project. It could easily be the follow-on project to this one - an HDMI interface for simple TTL computers.
-
Vivado
04/25/2017 at 22:22 • 2 commentsI've been using Vivado and been running into issues. The simulator crashes whenever I try to use the std_logic values L and H. They are supposed to be weak pull down and pull up values. I could see how they might not be supported, but the program shouldn't crash. Normally you'd never see those values inside an FPGA, but for simple behavioral simulation like I'm doing it shouldn't be an issue. I don't have access to Modelsim which I know can handle it, so I guess I'll make do.
-
Branching
04/24/2017 at 12:54 • 1 commentI've spent some time figuring out how to branch. The first problem is my program counter is 16-bits. So I figure I will have a temporary register hold the upper byte. This is written first. Then when the lower byte is written to, it will also copy over the temporary byte to load the PC all at once. So if you are jumping within the same page, you can copy the current upper byte over to this place, or if it's already there from a previous jump, then do nothing. So all jumps are absolute.
Next I think what I may try is to create a pointer register. So when I write to the pointer register, it contains a memory location. Then when I write to the a different address, instead of writing that data to that address, it writes to whatever address the pointer register is pointing to. So if my pointer register contains $00, then it will do a write to the $00 register next. Or if it's value is $01, the next data write goes to the $01 register. It overrides the destination register. This may be tricky to implement, but I'll worry about that later. If I make $00 a trash register and $01 the program counter, then that sets me up for my branching.
The next problem is how to "decide" when to branch and when not to. So most branches are "branch if equal to" or "branch if less than" type of decisions. They are tied to an ALU execution. I have the carry out bit and an operand equality bit. Both of these can be used to decide to branch or not. I can write the equality bit to it's own register. Then I can copy the contents of this register to the pointer register, and then write my branch address. In other words, to do a "branch if equal" command:
- Copy first value to ALU A register
- Copy second value to ALU compare address (this sets the equality bit in the equality register)
- Copy equality register to pointer register
- Copy branch address to pointer's target register (writes the branch address to either $00 or $01)
- So either the address was written to the trash register in which case nothing happens and the program counter falls through to its next location OR
- The program counter gets written to with a new address which executes the branch
The pointer register could be useful for many other things as well other than branching. You could have it target a place in memory dependent on an ALU result. For example, you want to write to a memory array. Add the array root memory location to an index to offset the memory location. And then write your data to that spot.
Anyway, I need to figure out how to do the implementation of the pointer register which might be tricky. I'll think about it while I finish up a few other things.
-
VHDL coming along
04/23/2017 at 22:38 • 1 commentSo I put together my RAM module in VHDL modeled after a generic SRAM. I was thinking of using an AS6C62256 from Alliance Memory. After a good bit of slugging it out with my VHDL code, I have a program counter connected to two RAM modules with registers on their outputs for the Source and Destination locations. I have a load register, ALU A register, and my ALU all hooked up to my data bus.
So I have my testbench loading a 3 line test code into the RAM which 1) writes a constant into the load register, 2) transfers from the load register into the ALU A register, and 3) adds the value in the ALU A register to the load register. And it works! (maybe a glitch or two still) It's a good start. I'm doing one-line VHDL address decoding for now, but I imagine I will eventually put them into EEPROMs. Next I'll finish the ALU output register and fix some glitches.
-
Memory interfacing
04/22/2017 at 17:47 • 0 commentsI've been thinking how to interface with the memory. I want a single memory bank. A lot of projects do separate instruction and data memory mainly because it's easier. But really no modern computers do this. I'd also like to have a page of the memory available for direct access. I can have 128 memory locations to move data into and out of. It's like having 128 registers.
So I'll need two memory pointers to access this page. And then I'll need two separate registers to store the program counter. It should also be possible to have them be the same. Then the page available is also the page executing code. I could pull addresses or constants right out of the instruction memory. Or do self-modifying code. Gives me lots of options which I really love.
-
Chip modeling
04/22/2017 at 02:13 • 1 commentGot my ALU and register modeled in VHDL. I admit I found a model for the 74181 online. But I wrote the '996 myself with a testbench. I was a little rusty on my VHDL, but it comes back fast. Vivado is a little hairy too. Took me a while to figure out that I can't start my module names with a number. But it's off and running now. Next I'll hook them up together and maybe even put a databus in there. A little address decoding and it starts to look like a system!
-
ALU functionality
04/21/2017 at 01:00 • 1 commentIf I'm using the '181, I could make all functions available to the compiler. I mean, there could be some efficiency gains in the more obscure functions. Since there's 4 bits for the function select pins, one for the mode pin, and a carry input, that's 6 control inputs. So I could have a separate address for each of the 2^6 or 64 possible functions. If only have 256 locations available for the 8-bit address bus, and half will be taken up by my RAM. So 128-64 = 64 remaining addresses. Should be plenty. It shouldn't be too difficult to decode considering I can take those 6 bits from the address line and run them straight to the ALU. It's a big gain for very little investment.
-
Register 54ALS996
04/20/2017 at 17:52 • 1 commentI've found my go-to 8-bit register - the 54ALS996. It has an 8-bit input for loading from the data bus. And the value can be read back on these same pins to be put back onto the bus. In addition, it has a second output port which can go to whatever other circuit like say the ALU. So it's like a two-port register with input and output on one port, and output-only on the second port. And it's an active chip. It's perfect. Thank you Texas Instruments for not obsoleting all of these old chips.
http://www.ti.com/product/SN54ALS996/description