Custom 8/16-bit CPU core design idea. Does this make sense? - Page 1
Forum section: electronics / general

author: technix
author_rank: Super Contributor
timestamp: October 10, 2016, 06:06:35 pm
content: Here are the specs I am thinking about. Do those make sense?* 8-bit ALU based on a pair of 74181s. Optional 16-bit ALU based on 4x 74181 and 1x 74182.* Princeton architecture with 8-bit data bus and 20-bit address bus.* RISC. 16-bit instruction words. Little endian* Unimplemebted instructions execute as nops.* 16-bit pointers with internal PXE MMU. 16 4kB pages.* The first 2kB in page 0 is not subject to MMU address translation and have a fixed RW0 access flag.* Any access to addresses 0x0000-0x001e guarantees an MMU interrupt.* All memory space within the first 2kB is guaranteed to be useable RAM unless being used as peripheral MMIO.* All internal peripherals (MMU, system timer, interrupt manager,maybe also an RTC) accessed through MMIO in the first 2kB of memory page 0.* 16 16-bit registers r0-r15, all of them can be accessed as high byte and low byte individually (as 32 8-bit registers)* r13 = stack pointer, r14 = return address pointer, r15 = program counter, r0h = 0, r0l = flags* nop instruction encodes as "ldi r0h, 0" 0xa100* Interrupts are level-triggered to the core.* Onboard programmable interrupt controller handles supplying the processor the vector number and managing interrupt priorities.* Interrupt vector table always reside at virtual address 0x0800, being an array of 16-bit words that the processor will call to. Unused words in the vector table can be used as general code or data storage.* Interrupts have almost the same behavior as function calls and return using the same instruction. (the only extra behavior in ISR calling is disabling global interrupt and raise to kernel mode) It is up to the called function or ISR to save the context.* The first instruction of a called function or ISR is guaranteed to be executed before an interrupt can trigger.* The first five interrupt vectors: Reset, NMI, Page Fault, System Timer and SysCall instruction.* Kernel mode code switch into user mode by writing a flag bit, but the other way around requires an interrupt.* Upon boot, the MMU starts with only the first RAM page mapped at address 0x0 with RX0 access flags.* Flags: 0, User mode, Global interrupt enable, 0, Zero, Carry, Overflow, Sign* MMU interrupts when 1) user-mode code accesses kernel-mode memory, or 2) access is made to nonexistant page, or 3) writing RX page or exeucting RW/R page.* Page states: 0) Nonexistant, 1) kernel-mode execute (RX0), 2) kernel-mode read (R0), 3) kernel-mode write (RW0), 4) User read kernel write (R1W0), 5) user-mode execute (RX1), 6) User-mode read (R1) 7) user-mode write (RW1). R1W0 is combined RW0 and R1.* MMU page map begins at address 0x0020. Each item consists of 2 bytes: Dirty, State[2..0], 4 application-specific flags, address prefix A[19..12].* MMU operates by taking the high 4 bits of virtual address as index to the MMU item, and combine the 8-bit page prefix and the remaining 12-bit virtual address to form a 20-bit physical address. This is how 16-bit virtual address is mapped into 20-bit physical address in the PAE MMU.Instructions (I can think of so far):* ALU instruction: 0, then 74181 function select bits M, S[3..0], then 8-bit destination register D[4..0], then 8-bit source register S[4..0].* Branch instruction: 1000 (jump) or 1001 (call) 4-bit condition mask M[3..0] and 4-bit condition code C[3..0] corresponding to the lower 4 bits on flags register, and a 16-bit target register D[3..0]. Jump or call if M == 0 or F[3..0] & M == C. Call instruction puts current PC into LR but jump don't.* Load Immediate: 101, then 8-bit register D[4..0], the 8-bit immediate to be loaded* Reserved 16-bit ALU opcodes: 110, then 74181 function select bits M, S[3..0], then destination register D[3..0], then source register A[3..0]. Those opcodes will be used if a 16-bit ALU is used.* 8-bit memory access: 1111000 (load), 1111001 (store), 1111010 (pop) or 1111011 (push), 8-bit data register D[4..0], 16-bit address register A[3..0]* 16-bit memory access: 11111000 (load), 11111001 (store), 11111010 (pop), 11111011 (push), 16-bit data register D[3..0], 16-bit address register A[3..0]* Return from interrupt or function call: 11111110, then 8 ignored bits.* Interrupt instruction: 11111111, then 8-bit immediate number that is not used by CPU (can be found out in code to implement a syscall number)MMIO peripheral map for the first 2kB:0x0000-0x003f: MMU0x0040-0x005f: PIC: interrupt mask0x0060-0x007f: PIC: interrupt flag0x0080-0x00bf: Memory bus interface (including ECC unit)0x00c0-0x00cf: system timer0x00d0-0x00df: real time clock and core NVRAM0x00e0-0x00ef: the first UART0x00f0-0x00ff: JTAG TAP0x0100-0x07ff: core ECC SRAM

author: C
author_rank: Super Contributor
timestamp: October 10, 2016, 06:51:19 pm
content: Popek and Goldberg virtualization requirementshttps://en.wikipedia.org/wiki/Popek_and_Goldberg_virtualization_requirements

author: Jr460
author_rank: Regular Contributor
timestamp: October 10, 2016, 07:12:41 pm
content: Quote from: technix on October 10, 2016, 06:06:35 pmThe first page in many OSes is set to no access even by the kernel for a good reason. Programming errors when you access a member of a data structure and you point to it with a null pointer. Typos, I wanted to move literal "zero" into a register, not the data at location zero.

author: technix
author_rank: Super Contributor
timestamp: October 11, 2016, 02:11:38 am
content: Quote from: Jr460 on October 10, 2016, 07:12:41 pmI can put a special case here: any access to first 31 bytes will always result in a memory management interrupt. (This will not knock the MMU structure out of alignment: byte 0-30 = error, byte 31 = MMU status register, now I am back in 32-byte alignment and can put an aligned page table there.)

author: C
author_rank: Super Contributor
timestamp: October 11, 2016, 02:59:20 am
content: Each mode should have it's own stack pointer.If you have kernel-mode & user-mode each should have it's own stack register, can be addressed as same register most of time. note there are times when kernel-mode needs to read/set user-mode stack register.MMU should have a dirty bit (this block has been written to) if you want to swap to disk capability.What are you thinking r14 = return address pointer.sounds like a one call level limit in hardware. Pushing return address on stack allows many levels of recursion.

author: helius
author_rank: Super Contributor
timestamp: October 11, 2016, 03:21:20 am
content: Quote from: C on October 11, 2016, 02:59:20 amEach function generally knows whether it is a leaf. That's why RISC architectures use Link Registers and callee-save of return addresses instead of mixing up call and stack operations.

author: technix
author_rank: Super Contributor
timestamp: October 11, 2016, 09:04:25 am
content: Quote from: helius on October 11, 2016, 03:21:20 amThat is the spirit, Upon calling or interrupting the link register is set to the current PC and it is up to the caller or ISR to protect the context. At least one instruction is executed after a function call, so as long as the first instruction in a function is "pushw lr, sp" you are safe. And you can return with "popw pc, sp" if you so desire (although it is still recommended to "popw pc, lr; ret" instead of that)

author: helius
author_rank: Super Contributor
timestamp: October 11, 2016, 11:09:38 am
content: I was saying that it's a common approach, not necessarily endorsing it: one problem is that it's harder to do software interrupts (signals) if every function is not always ready to call out. You need to make special trampolines in assembly code that arrange for the interrupted function's state to be restored.

author: C
author_rank: Super Contributor
timestamp: October 11, 2016, 12:17:20 pm
content: I do not see a big benefit of this design over others and each design has it's good and bad points..Some DEC's PDP11's had memory management units that allowed 4Mb ram.The PDP11 was a real work horse.PDP11-344 741818 16 bit registers with 4 modes of useMicrocodedIt is an asynchronous CPU not clock based.lots of areas that can change to make it faster while keeping same software.You have2 7418116 16 bit registersRISC architectures which normally = more instruction fetch cycles.Both your's and PDP11 have a 16 bit instruction.What did you remove to get the 2 added bits in the instructions?What makes Your idea better & what is it's fails?Note for a PDP11 it is easy and fast to use any register as a stack pointer.

author: ale500
author_rank: Frequent Contributor
timestamp: October 11, 2016, 02:01:34 pm
content: The schematics for the PDP11/45 and PDP11/70 are available at bitsavers. I think often of doing something like that, but with the amount of logic involved, and not wanting to do a pure fpga design, a board with some CPLDs may be an intermediate approach. Look at this MIPS implementation with TTL logic (in German):http://www.fritzler-avr.de/spaceage2/index.htm

author: rstofer
author_rank: Super Contributor
timestamp: October 11, 2016, 05:09:29 pm
content: Quote from: ale500 on October 11, 2016, 02:01:34 pmI have messed around building up the ALU for an 11/70 using an FPGA but I didn't bother to build the rest of the machine because, without software, the hardware is useless. If I were to build a retro CPU, I would first want to have the OS and compilers, editors and assembler. Some of this stuff is out there. It's a matter of getting the software nailed down before starting with the CPU.That said, Unix V6 is very well known and the OS code has been published. It should be possible to build up a system capable of supporting the OS even if it isn't strictly a PDP11. There are a number of these projects out there and I built one up a few years back. I didn't design any of the project, I just copied and pasted.

author: technix
author_rank: Super Contributor
timestamp: October 11, 2016, 05:41:43 pm
content: Quote from: rstofer on October 11, 2016, 05:09:29 pmI think you can still unearth an ancient verison of GCC that 1) builds under modern OS and 2) targets PDP-11. Or maybe you can find a version of ancient GCC that can compile the version of C compiler that came with the released UNIX Version 6 code and targets PDP-11. Either way you get a cross compiler that can run on modern UNIX and spit out PDP-11 code.If you are feeling adventurous you can try port the ancient PDP-11 backend to the state-of-the-art GCC 6.x, or start a new LLVM backend targeting PDP-11 for clang (so C11 and C++11 can target PDP-11.)

author: ale500
author_rank: Frequent Contributor
timestamp: October 12, 2016, 04:34:35 am
content: The compiler for v7 is available in old C, just the assembler was written in pdp-11 assembler. Maybe the output is Macro11 compatible...I saw the sources of v5, I never saw the sources of v6, and I cannot remember about v7. Did you ?

author: technix
author_rank: Super Contributor
timestamp: October 12, 2016, 10:30:35 am
content: Quote from: ale500 on October 12, 2016, 04:34:35 amI know that whoever owned the copyright to Unix Version 6 released it under an open source license (BSD?) so I know that the code is definitely out there.

author: rstofer
author_rank: Super Contributor
timestamp: October 12, 2016, 04:54:42 pm
content: https://en.wikipedia.org/wiki/Lions%27_Commentary_on_UNIX_6th_Edition,_with_Source_Code

author: legacy
author_rank: Super Contributor
timestamp: October 12, 2016, 06:02:38 pm
content: Quote from: technix on October 11, 2016, 05:41:43 pmLoL

author: C
author_rank: Super Contributor
timestamp: October 12, 2016, 06:55:36 pm
content: If you bother to look back, other CPU's existed at that time.Compared to some, the PDP11 CPU was easy to understand and or had more options. Result was better source code input.This also overlooks the hardware side. Simple things like arranging bits in control and status registers to make software easer and faster. The simple idea of two addresses for some registers. One equaled peak at data, the second being get the data while telling hardware data has be read. Using many vectored interrupts for one device.For you unix fanboysguess which is better unix V6 or V7 compared to ULTRIXAll are a fail or a lot of work with out matching hardware.In some cases RSX11M was best choice.https://en.wikipedia.org/wiki/16-bit

author: ale500
author_rank: Frequent Contributor
timestamp: October 13, 2016, 04:43:02 am
content: When I said, that I didn't finf the source of v6, I was expecting a tape file for the pdp-11 with it.A better start point may behttp://retrobsd.org/wiki/doku.php: it compiles on a modern gcc.

author: ale500
author_rank: Frequent Contributor
timestamp: October 13, 2016, 05:09:18 am
content: As a starting point, before you wire up all components, you could use rtl or behaviour modeling (in VHDL or Verilog, ther are others): That way you can test your architecture before you commit to it.

author: technix
author_rank: Super Contributor
timestamp: October 13, 2016, 09:56:51 am
content: Quote from: ale500 on October 13, 2016, 05:09:18 amThis design, if used, will be a FPGA-based implementation anyway. So I'll just flash it into a FPGA dev board and pump some code in to test it.

author: ale500
author_rank: Frequent Contributor
timestamp: October 13, 2016, 12:19:06 pm
content: Count me on !

author: technix
author_rank: Super Contributor
timestamp: October 13, 2016, 12:27:18 pm
content: Quote from: ale500 on October 13, 2016, 04:43:02 amI actually prefer creating a new backend for it in some modern compiler like GCC 6 or LLVM. This way the code generated through such a modern optimizing compiler can be fore efficient on even older architectures like PDP-11. Bonus point for LLVM as its optimization algorithm can work reasonably well on any architecture it can target, if the target offers the optimizer information about the tricks that is available on the platform.

author: legacy
author_rank: Super Contributor
timestamp: October 13, 2016, 02:26:14 pm
content: Quote from: technix on October 13, 2016, 12:27:18 pmI actually wonder IF you have ever and successfully done something

author: nctnico
author_rank: Super Contributor
timestamp: October 13, 2016, 03:11:17 pm
content: Quote from: technix on October 13, 2016, 09:56:51 amI'd go the simulation route first and then put it into an FPGA. In simulation it is much easier to debug than when it runs inside an FPGA.

author: technix
author_rank: Super Contributor
timestamp: October 13, 2016, 03:14:22 pm
content: Quote from: nctnico on October 13, 2016, 03:11:17 pmNoted. Thanks for the tips.