-
Some great computer-builder resources
06/17/2023 at 18:37 • 0 commentsPDP-11 compatible
Really impressive work reverse engineering and re-building using modern HDL tooling of computer build from Am2901-compatible bit slices.
-
In-circuit testing using Intel HEX file components
12/04/2021 at 19:47 • 0 commentsSome background...
The main purpose of my Intel HEX project was to:
- Check how difficult it is to create non-CPU controllers using microcode
- Improve my microcode compiler based on real project
- Give me a tool for priming memory of FPGA-based computers when CPU is not working / written yet - during runtime, avoiding slow recompiles
Turns out, with some breadboards, wiring, and bringing the Intel HEX component buses outside from FPGA into the physical world, the same components can be reused for a custom and handy testing tool.This testing allows to check:
- Address, data, some control (R/W) buses
- (EP)ROM/RAM if populated (for expected content or read / write / re-read)
- Address decode logic (e.g. mapping of ROM/RAM, repeats due to partial decode etc.)
Note that boards / computers tested this way do not need to be populated fully with ICs or other components, so it is possible to check "dead" or "not yet alive" boards too.
There are two possibilities:
- CPU is not on the board, "insert" the wires into CPU socket (poor man's in-circuit emulator)
- CPU is on the board, but there is bus access that allows DMA takeover (e.g. BUSREQ/BUSACK in Z80-type buses)
What cannot be readily tested using this approach:
- Non-memory oriented bus signals (e.g. clocks, interrupts etc.)
- I/O (memory mapped) - there can be indication that it "kinda works" as those spaces will be read as whatever default the I/O device returns, and in case of write in some simple case (e.g. write latch connected to some LEDs or relays) it may work.
- I/O (separate) - with extra signal I/O space can be "forced" to appear as memory space and then it can be figured out if the data returned makes sense or not (example given below)
Hardware
For this project I used a cool little 8085-based single board computer (8085 Minimax) described and graciously provided to me by Ken Yap (thanks again!). I was actually in the process of soldering together the board, and decided to use a verification step before plugging in my vintage Soviet CPU to see if there will even be a chance for it working or not...
The hardware setup is simple but a bit messy affair:
Few notes:
- 8085 IO/M signal (purple wire) is simply switchable to IO (high) to M(emory) (low) using a micro-DIP on the FPGA board. The HEX I/O has no idea about it, it just sees IO space as another 64k address map space (of course with 256 repetitions because typically in 8080-family systems upper 8 address lines are not decoded (Z80 introduced 16-bit IO to some degree, and then HD64180 has a full 64k IO address map)
- 8085 has multiplexed lower address A7..A0 with data bus D7..D0, giving AD7..AD0 bus. Typically a 8-bit latch (like 74x573) enabled by ALE captures the low address early in memory access cycle. To simplify, I left this IC unpopulated on the board and connected the low memory address wires (gray) directly to its output socket pins.
- Minimax 8085 of course takes +5V DC - I am sourcing its modest consumption (esp. because the power hungry NMOS CPU is not there!) with a 3.3V to 5V step-up regulator.
- Connecting nominal 3.3V (FPGA) to nominal 5.0V (SBC) is a "circuit crime". But I could get away with it in this case as the modern RAM / ROM used has max "0" voltage and min "1" voltage signals that are within margins.
- 4 additional PMOD signals are used for UART - this is how the Intel HEX files are uploaded/download during runtime.
This is how these connections look in VHDL (top file of the project):
--PMOD interface JA1: inout std_logic; -- Connected to USB2UART JA2: inout std_logic; -- Connected to USB2UART JA3: inout std_logic; -- Connected to USB2UART JA4: inout std_logic; -- Connected to USB2UART JB1: out std_logic; -- GRAY 74F573.19 A0 JB2: out std_logic; -- GRAY 74F573.18 A1 JB3: out std_logic; -- GRAY 74F573.17 A2 JB4: out std_logic; -- GRAY 74F573.16 A3 JB7: out std_logic; -- GRAY 74F573.15 A4 JB8: out std_logic; -- GRAY 74F573.14 A5 JB9: out...
Read more -
More microcode compiler capabilities!
09/15/2021 at 06:48 • 0 commentsI have been fairly busy with the microcode compiler, mostly because the new HEX IO project has brought new ideas about useful features.
In addition to various bug fixes, the main one is addition of new .sym directive. Here is an example:
.symbol 7, 256, hex2mem_sym.mif, hex2mem_sym.cgf, hex2mem_sym.coe, h2m:hex2mem_sym.vhd, hex2mem_sym.hex, hex2mem_sym.bin, 32;
.symbol 7, 256, hex2mem_sym.mif, hex2mem_sym.cgf, hex2mem_sym.coe, h2m:hex2mem_sym.vhd, hex2mem_sym.hex, hex2mem_sym.bin, 32;
This will do the following:
- Create internally a memory block 7 address lines deep (128 locations) and 256-bits wide
- Initialize the block with source code of the microcode (8-bit ASCII, everything on the line beyond byte 32 will be truncated)
- Export the memory block into file names / formats specified (for example, hex2mem_sym.hex file)
- Generate .vhdl template file that can be directly included into project if needed
The vhdl template file can be consumed by a component built into the project during debug and development phase to provide tracing of source code at each microinstruction being executed. Here is an example of such a tracer. For each microinstruction, it generates a 64 ASCII char record (close with CR+LF chars) suitable for UART transmit. This record typically contains:
- some number of internal machine values or flags useful to observe the state of the system
- microinstruction pointer value
- source code line
Here is an example of all microinstructions executed by hex2mem component when a ':' (first char in hex file line) is received:
. : 3A 67 trace: if TRACE_CHAR then next e . : 3A 1E emitChar: if TXDREADY then next I : 3A 20 if TXDREADY then next else repea I : 3A 69 if false then emitChar else emit I : 3A 1F if TXDREADY then next else repea = : 3A 21 if TXDSEND then return else retu = : 3A 1E emitChar: if TXDREADY then next 3 : 3A 20 if TXDREADY then next else repea 3 : 3A 6B if false then emitChar else emit 3 : 3A 1F if TXDREADY then next else repea A : 3A 21 if TXDSEND then return else retu A : 3A 1E emitChar: if TXDREADY then next : 3A 20 if TXDREADY then next else repea : 3A 6D if false then emitChar else emit : 3A 1F if TXDREADY then next else repea A : 3A 21 if TXDSEND then return else retu A : 3A 1E emitChar: if TXDREADY then next = : 3A 20 if TXDREADY then next else repea = : 3A 6F if false then printaddr else pri = : 3A 1E emitChar: if TXDREADY then next 0 : 3A 20 if TXDREADY then next else repea 0 : 3A 2A if false then emitChar else emit 0 : 3A 1F if TXDREADY then next else repea 7 : 3A 21 if TXDSEND then return else retu 7 : 3A 1E emitChar: if TXDREADY then next 9 : 3A 20 if TXDREADY then next else repea 9 : 3A 2C if false then emitChar else emit 9 : 3A 1F if TXDREADY then next else repea 0 : 3A 21 if TXDSEND then return else retu 0 : 3A 70 if false then emitChar else emit 0 : 3A 1F if TXDREADY then next else repea : 3A 21 if TXDSEND then return else retu : 3A 1E emitChar: if TXDREADY then next C : 3A 20 if TXDREADY then next else repea C : 3A 72 if false then emitChar else emit C : 3A 1F if TXDREADY then next else repea = : 3A 21 if TXDSEND then return else retu = : 3A 1E emitChar: if TXDREADY then next 0 : 3A 20 if TXDREADY then next else repea 0 : 3A 74 if false then emitChar else emit 0 : 3A 1F if TXDREADY then next else repea 0 : 3A 21 if TXDSEND then return else retu 0 : 3A 1E emitChar: if TXDREADY then next 0 : 3A 20 if TXDREADY then next else repea 0 : 3A 76 if false then emitChar else emit 0 : 3A 1F if TXDREADY then next else repea 0 : 3A 21 if TXDSEND then return else retu 0 : 3A 1E emitChar: if TXDREADY then next : 3A 20 if TXDREADY then next else repea : 3A 78 if false then emitChar else emit : 3A 1F if TXDREADY then next else repea B : 3A 21 if TXDSEND then return else retu B : 3A 1E emitChar: if TXDREADY then next = : 3A 20 if TXDREADY then next else repea = : 3A 7A if false then emitChar else emit = : 3A 1F if TXDREADY then next else repea 3 : 3A 21 if TXDSEND then return else retu 3 : 3A 1E emitChar:...
Read more -
From MUXs to BASIC - working on documentation
06/14/2020 at 18:57 • 0 commentsFound time to work on documentation! To exact surprise of nobody, creating good documentation is at least as hard as creating good software, if not more. One interesting aspect is that through the process of documentation, one has to revisit design decisions and if something sounds clunky or counter-intuitive to describe, then probaby it reall is, and should be improved!
-
Some progress on microcode compiler in C#
04/28/2020 at 19:56 • 0 commentsFor some news, check also:
https://hackaday.io/page/11244-more-microcode-compiler-capabilities
So after some time, I was able to find some time to work on the "MCC" (microcode compiler"). The compiler is now mostly written and is able to compile .mcc files (in a "language" I invented in order to make microcode writing simpler and more intuitive).I plan to eventually create a separate project for it with more extensive documentation, but for now here is a excerpt from the microcode for CDP1802/5/6 CPU I use for validation, which should give some idea:
//-------------------------------------------------------- // 1802 basic instructions //-------------------------------------------------------- .map 0b0_0000_????; // D <= M(R(N)) LDN: direction = mem2any, sel_reg = n, y_bus, reg_d <= alu_y, if continue then fetch else dma_or_int; .map 0b0_0000_0000; // override for LDN 0 IDL: sample; // dead loop until DMA or INT detected if continue then IDL else dma_or_int; .map 0b0_0001_????; // R(N) <= R(N) + 1 INC: sel_reg = n, reg_inc, if continue then fetch else dma_or_int; .map 0b0_0010_????; // R(N) <= R(N) - 1 DEC: sel_reg = n, reg_dec, if continue then fetch else dma_or_int; .map 0b0_0011_????; // 2 byte branch instructions SBRANCH: direction = mem2any, sel_reg = p, y_bus, reg_b <= alu_y, // B <= R(P) if cond_3X then sbranch2 else next; skip1: sel_reg = p, reg_inc, // R(P) <= R(P) + 1 (no branch) if continue then fetch else dma_or_int; sbranch2: sel_reg = p, reg_lo <= alu_y, y_b, if continue then fetch else dma_or_int; .map 0b0_0100_????; // D <= M(R(N)), R(N) <= R(N) + 1 LDA: direction = mem2any, sel_reg = n, y_bus, reg_d <= alu_y, goto INC; .map 0b0_0101_????; // M(R(N)) <= D; STR: direction = cpu2mem, sel_reg = n, y_d, if continue then fetch else dma_or_int; .map 0b0_0110_0???; OUT: sel_reg = x, reg_inc, direction = mem2any, // DEVICE <= M(R(X)), R(X) <= R(X) + 1 if continue then fetch else dma_or_int; .map 0b0_0110_0000; // override for OUT 0 IRX: sel_reg = x, reg_inc, // R(X) <= R(X) + 1 if continue then fetch else dma_or_int; .map 0b0_0110_1???; INP: sel_reg = x, y_bus, reg_d <= alu_y, direction = dev2any, if continue then fetch else dma_or_int;
The compiler produces 2 blocks of memory, one for the microcode, and one for the mapper that maps higher level instructions (from CPU instruction set). These blocks are then written in possible formats:
- VDHL - for direct inclusion into FPGA development projects (I use old "free" ISE14.7 from Xilinx)
- Intel HEX - pretty much any tool can consume these
- MIF ("memory initialization file") for Altera tools
- CGF ("core generator file") for Xilinx tools
At this point I am writing / debugging the 1802 CPU which I intend to eventually test using the embedded software (monitor/Basic) documented here. Once it starts (somewhat) working, I will share that project too.