ECLair

From Hackstrich
Revision as of 18:31, 1 April 2018 by SarahEmm (talk | contribs) (Documentation said this was little-endian, it's actually big.)

ECLair is a long-term project to build an ECL minicomputer. All code/formal documentation/utilities can be found in the ECLair GitHub Repository.

Project Status

  • 2018-03-31: Thought a lot about whether 64k per page table block is really enough space, considered going to 9bit bytes and 18bit alu/registers, decided against it after a lot of consideration as I think it would make interop/testing/etc. much harder. Going to stick with 64k per PTB for now, and can work around it later if need be (giving processes multiple pages using code overlays and such).
  • 2018-03-28: Fixed a significant bug in the functional simulator test functionality, was testing at the wrong moment and also skipping tests at HALT time. Both fixed now, and sub8.ab passes all tests (was failing ~60% before).
  • 2018-03-27: Test Console in the functional simulator now mostly functional, a couple loose ends to tie up (it doesn't appear automatically when a test is loaded, and you have to manually show it before loading the ROM).
  • 2018-03-26: Started working on a separate Test Console window that lists all the tests and their status.
  • 2018-03-25: Implemented the rest of basic test functionality, simple test now run successfully.
  • 2018-03-24: Finished implementing paging in the functional simulator. Started implementing functionality to run the same test cases the Verilog simulator does, all the functionality to read ASCII-binary test files and build a list of test steps is done, as is most of the test runner, just need to get it integrated now.
  • 2018-03-23: Finished migrating the memory display to a table and implementing highlighting of where pc currently points. Started adding a page table display as well.
  • 2018-03-22: Added a diagram of how paging is set up, started working to move the memory display from a text box into a proper table so we can do highlighting and such, not done this yet but getting close.
  • 2018-03-21: Implemented basic STATUS/FLAGS support in the functional simulator and made numerous bug fixes so now the jmp and jmpz tests now run successfully, as does the mode test. Next up is implementing paging, which first requires I re-learn how paging actually works (and create some better documentation so people other than me, or me in the future, can someday understand).
  • 2018-03-20: Refactored the way control words are decoded, much simpler/more readable code now. Next steps will be to complete support for the rest of the microcode including paging, and then implement the microcode disassembler properly (rather than hardcoding it all like it is now).
  • 2018-03-19: Improved the code for the functional simulator some, made it more "Swift-like". Also used it to make a slight optimization to ldi16.?, it was doing an extra memory read that wasn't required, so eliminated that and all tests still pass.
  • 2018-03-18: More work on the functional simulator, now executes the ldi16.a test from start to finish without issues. Still doesn't support paging, any of the FLAGS, and a lot of other microcode features.
  • 2018-03-17: Worked on the simulator a bunch more, it now gets 3 microcode steps in before dying, which is progress!
  • 2017-07: Started moving the basic Swift simulator into XCode and building a GUI for it.
  • 2016-12: Started working on a high-level simulator to use in working out the final instruction set before finalizing hardware. Learning Swift as I do it, so progress is not super fast.
  • 2015-07-03: Realized there's no reason to have a Z register at all, X and Y need it since they share a common input bus (XY), but every time we load Z we immediately then use the result from Z to load elsewhere. Eliminated it and all tests still pass.
  • 2015-07-01: Wrote a thing that generates digraphs out of the microcode. Did a bunch of prep work for doing more interrupt work. Now have instructions to disable/enable interrupts and can individually set/reset FLAGS bits (instead of having to do all at once like before). Realized I need a read path for FLAGS so that the interrupt microcode can push/pop them, but out of inputs on the XY muxes, so will have to figure that out.
  • 2015-06-30: Finished converting all of the unit tests to seasm. Implemented the basic hardware so that interrupts are able to force a jump to microcode interrupt vectors rather than IR when a fetch/execute next happens. Also moved ECLair into a test harness which can poke interrupts, which will make writing a unit test possible. Next step is to write the microcode to actually handle this.
  • 2015-06-28: Started working at moving the unit tests over to seasm rather than raw binary, most of the 8bit-operand tests are moved over, the "assembler" doesn't support 16-bit operands yet so that will be the next step to getting all the tests moved over.
  • 2014-06-03: call/ret are finally done and have passing unit tests! Started work on interrupt logic, no inputs left on the X/Y mux to get the interrupt vectors in, added a 4-bit mux on the A port to select either immediate bits or highest active interrupt vector. Next step is to start wiring up the actual interrupt flag gates and clearing logic.
  • 2014-06-01: Added realistic propagation delays to alu4 and finished the RAM delays, tests still all pass at 25MHz and 33MHz now. Leaving the clock at 25MHz for hte time being, as that's the actual goal (though 33MHz would be nice). Wrote a super simple assembler (if you can call it that) which pulls instructions/locations from microcode.txt and lets you write text instead of ones and zeroes. Should rewrite the tests to use this at some point, will make troubleshooting them a lot easier. Still avoiding working on CALL/RETURN >_>
  • 2014-05-31: sub8.ab now works and passes all its unit tests (and added some more unit tests for it, to be safe/confident). Next step is finishing CALL/RETURN. Started working on this, realized that since CALL takes 2 immediate bytes currently, RETURN returns into the middle of it. CALL should probably push the return address, not its own address. Added realistic propagation delays for the clag4, added simplified worst-case propagation delays to the alu4 and a bunch of tests start failing. Backed those ones out for now, will finish implementing CALL/RETURN then go back and finish the delay/timing work.
  • 2014-05-30: pop16.pc now working/passing its unit tests, sub8.ab still being worked on.
  • 2014-05-18: Worked on pop16.pc microcode and sub8.ab microcode. Neither working yet.
  • 2014-05-06: Figured out register flow requirements for pop16.pc, documented. Next step is to implement the test and microcode for it.
  • 2013-12-29: Implemented include functionality in mcgen.rb so that instruction like call which are really just push16.pc then jmp can be written like that, rather than having to copy/paste the contents of those other instructions.
  • 2013-12-26: Implemented CALL and extended mcgen.rb to generate a mapping file from control store address to instruction, which can be loaded into gtkwave. Next steps are to implement POP and RET.
  • 2013-05-05: Refactored all microcode instructions and their unit tests so that PC now equals the instruction currently being executed, and not the memory location after that. Implemented push16.pc and its unit test. Next step is to implement CALL.
  • 2013-05-04: Reworked PUSH into push8.imm and push8.? (for future implementation of other non-immediate push8s). Implemented push16.imm and push16.? including their unit test and register transfer diagram. Started working on push16.pc, but ran into an issue where PC is incremented within FETCH, so if we push it as part of push16.pc then we're actually pushing PC+1. Need to figure out if that's okay, or if we need to rework where inc_pc is triggered in the fetch process.
  • 2013-02-24: Finished implementing PUSH, including the unit test for it. All working now, and fixed a lot of bugs related to RAM while doing so, as this is the first use of RAM in the system. Next step is to go back to working on CALL/RETURN now that PUSH is working.
  • 2013-02-20: Started working on the unit test for PUSH, found that I hadn't ever implemented the SP register. Did so, and troubleshot a bunch around that. Test still not passing, more troubleshooting required.
  • 2013-02-19: Added proposed microcode for PUSH, which is a prerequisite for CALL/RETURN. Next step is to write a useful unit test for it.
  • 2013-02-17: Figured out/diagrammed initial interrupt logic. Next step is to implement CALL/RETURN, which will be needed to integrate it into the system.
  • 2013-01-16: Added supervisor/user mode support and a test for it. All working. Still need to write a branch-if-CO unit test.
  • 2013-01-08: Added conditional branch support and a test for it. Currently only have a test for the branch-if-Z path, not the branch-if-CO path. Next step is to add that branch-if-CO unit test, then probably work on fault/trap/interrupt support.
  • 2013-01-06: Added support for ALU status bits Z (last operation result was zero) and OC (overflow/carry out) and tests for them. Also added add8.ab. Next step is adding conditional branch support.
  • 2013-01-05: Wrote a new unit test system, and implemented tests for all current instructions. Next step is probably to look at implementation of ALU status bits.
  • 2012-12-26: Implemented ldi8.flags so that paging can be turned on. Fixed a couple bugs in the system, now paging works! Also renamed PID to PTB (page table block) as one PID could potentially use more than one page table entry. Still need to figure out how to automatically test this.
  • 2012-12-25: Worked out details on the paging system, added it to the schematic, implemented it. Can now ldi8.pid to load the current PID into the PID register, then wrpte to load the first entry in the page table. Can't yet switch paging on, as status/flags isn't implemented yet. Next step is figuring out how to integrate paging mechanism into the automated tests, then implement status/flags so paging can actually be turned on.
  • 2012-12-23: Worked out the right way to fix the sequencing issue/dependency between microcode signals, now the level-sensitive signals (bits 0-23) latch first, then the edge-sensitive signals (bits 24-63) latch, then the edge-sensitive signals are reset back to 0 so they don't mis-trigger when the level-sensitive signals change again on the next cycle. This means all edge-sensitive signals need to be active-high, but that hasn't been an issue so far.
  • 2012-12-22: Hacked on the sequencing issue more, no real progress.
  • 2012-12-21: Reordered microcode bits into 24 bits for edge-sensitive signals and 40 for level-sensitive signals. Redesigned the microcode sequencer to use a latch and an always-populated next_addr bit field rather than a counter, which solves many problems around jumping and runt cycles. Still don't have sequencing of the two classes of microcode bits worked out, it's proving a much harder problem than I'd expected.
  • 2012-12-20: Implemented realistic propagation delays for most parts (main EPROM and ALU not done yet), and fixed a bunch of bugs related to previous lack of delays. Have a hacked-in delay for cs_jump right now, next step is to split the microcode into 24 "edge-sensitive signal bits" and 40 "level-sensitive signal bits", and clock the former slightly after the latter. This will avoid needing two separate microcode instructions to set up level-sensitive signals then trigger the edge-sensitive ones. Will mean a complete reorganization order-wise of the microcode though.
  • 2012-12-19: Finished implementing 8/16-bit width selection and got the bugs worked out. Now have 8-bit and 16-bit immediate loads, and 8-bit don't trash the other 8 bits of the destination register. Still need to get 8-bit high-byte load implemented. Next step though is probably to rethink the hold_last function in the microcode assembler, should probably make it more intelligent to only hold level sensitive signals and leave the edge-sensitive ones out.
  • 2012-12-18: Half-implemented 8/16-bit width selection using a microcode bit. Doesn't work yet.
  • 2012-12-17: Implemented a data path from IR[7:6] to the register latch signals, so that all register-related operations can be simplified in microcode. Moved the ldi* instructions to use this new path, everything is tested and working. Next step is probably to get the 8/16-bit split working, so that 8-bit operations don't erase the other byte of the word.
  • 2012-12-16: MDR-XY data path implemented, Load Immediate 8-bit values to A and B instructions written, add 16-bit A=A+B written, all working.
  • 2012-12-15: Found bug in the datasheet of the MC10H181, fixed it and now the ALU checks out completely. Started integrating it into the CPU. Next step is to finish the MDR-XY data path so that Load Immediate instruction can be implemented, then Add can be implemented/tested.
  • 2012-12-14: Verilog transcription of ALU almost complete, but malfunctioning in the carry logic of the second bit. Will be using MC10H181 w/ MC10H179 CLA generator.
  • 2012-12-09: Started working on ALU design/verilog transcription.
  • 2012-12-08: Added decode support for ROM/RAM/device memory, got everything working so that now an instruction is fetched from ROM at startup and is jumped to in microcode. Started work on the X/Y registers, and started work on a microcode generator so I can stop typing in individual bits.
  • 2012-12-05: Got the part that's been diagrammed into Verilog and working. Now fetches instructions (from a constant right now, not RAM yet) and does jumps to their location in microcode.
  • 2012-12-04: Started the logical diagram and the microcode layout.
  • 2012-12-01/02: Spent the weekend reading Bit-Slice Microprocessor Design.
  • 2012-11-24: Basic ideas/architecture starting to get put together.

Architecture Overview

  • Many blinkie lights and switches on the front panel
  • MECL-based (10KH/10KE levels/speeds)
  • 25MHz main clock (hopefully)
  • 8-bit data width
  • 24-bit physical address, 16-bit virtual address
  • DMA support (for front panel, maybe for storage)
  • Microcoded, running control store in SRAM for speed
    • Unless I can find equally-fast EPROMs
    • Copied to SRAM from EPROM before the system releases reset on powerup
    • Could implement a writable control store using this too
  • 8 interrupt lines (heartbeat, RTC, serial 0, serial 1, storage, 3 for future use)
  • Big-endian

Registers

Name Width (bits) Function
PC 16 Program Counter
SP 16 Stack Pointer
PTB 6 Page Table Block
IR 8 Instruction Register
MAR 16 Memory Address Register
MDR 16 Memory Data Register
FLAGS/STATUS 16 System flags and status bits (8 bits for R/W flags, 8 for R/O status)
A 16 General Purpose
B 16 General Purpose
C 16 General Purpose
D 16 General Purpose
X 16 ALU Input
Y 16 ALU Input


Memory Map

  • 24bit / 16MB address space
  • 14MB for RAM, 1MB for ROM, 1MB for memory-mapped devices
  • 0x000000 - 0x0FFFFF - ROM
  • 0x100000 - 0xEFFFFF - RAM
  • 0xF00000 - 0xFFFFFF - Devices

Page Table

  • 64 process slots available in page table
  • Each process gets access to 64 pages
  • Each page is 1K long
  • Bit 15 (MSB) in page table entry is 1 if page is present
  • Bit 14 in page table entry is 1 if page is writable
  • Address Routing
    • Virtual address - 16 bits
      • Bits 15-10 - Page Select - passed to page table address bits 11-6
      • Bits 9-0 - Address - passed to ADDR LSB as-is
    • Physical address - 24 bits
      • Bits 23-10 - Page - passed from page table data bits 13-0
      • Bits 9-0 - Address - passed from virtual address bits 9-0

ECLair Paging.png

Flags/Status

High Byte - Flags

  • Bit 9 - PE - Paging Enabled
  • Bit 8 - M - Mode (User/Supervisor)
  • Bit 7 - IE - Interrupts Enabled

Low Byte - Status

  • Bit 1 - CO - Carry/Overflow
  • Bit 0 - Z - Last Result is Zero

Microcode Layout

Bit # Width Function Details
Edge Sensitive Signals
0 1 Write Page Table Entry
1 1 MDR Load Latches data or Z (determined by bit 34) into byte of MDR (determined by bit 24)
2 1 AVAILABLE FOR USE
3 1 MAR Load MAR latch load (source specified by bit 33)
4 1 IR Load IR latch load from data bus
5 1 PC Increment Increment PC by 1
6 1 PC Load PC counter load from Z
7-9 3 Register Load from Z 000 - None
001 - A
010 - B
011 - C
100 - D
101 - SP
111 - Value from IR[6..7] (only A-D)
10 1 X Load
11 1 Y Load
12 1 AVAILABLE FOR USE
13 1 PTB Load
14 1 AVAILABLE FOR USE
15 1 Load Status from ALU
16 1 Clear Interrupt Flags from Z 1 bit in Z clears that interrupt flag
17 1 Memory Write
18 1 Load Flag PE Loads PE flag from Z[0]
19 1 Load Flag M Loads M flag from Z[1]
20 1 Load Flag IE Loads IE flag from Z[2]
Level Sensitive Signals
24 1 MDR Byte Select low = low byte for 8bit ops, high = high byte for 8bit ops
25-32 8 Next CS Address 0 = Use IR
33 1 MAR Source low = Z, high = PC
34 1 MDR Source low = Z, high = data bus
35 1 ALU Mode 0 = Arithmetic, 1 = Logic
36-39 4 ALU Operation 1111 - Z=X
40-42 3 X/Y Register Source 000 - Immediate Value (LSB from bit 24)
001 - A
010 - B
011 - C
100 - D
101 - SP
110 - MAR
111 - MDR
43 1 Memory Read
44 1 Carry In Carry input for the ALU. Used to carry in a 1 to let the ALU subtract.
45 1 Operation Width 0 = 8-bit, 1 = 16-bit
46-48 3 Branch Condition 000 - Unconditional
001 - Z
002 - OC
49-50 2 X/Y Immediate LSBs Used if 40-42 = 000
51 1 XY Register Nibble Select 0 - XY Mux A input connected to immediate bits
1 - XY Mux A input connected to active interrupt vector
52 1 Register Byte Select low = low byte for 8bit ops, high = high byte for 8bit ops