YCPU: an imaginary 16-bit processor.

You can talk about almost anything that you want to on this board.

Moderator: Moderators

lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: YCPU: an imaginary 16-bit processor.

Post by lidnariq »

pops wrote:although I initially decided on 4 KiW, I'm aware that everything from context switching to loading large arrays of data would be sped up by larger pages (perhaps 8 KiW or 16 KiW). Programs that had large global sets of data or functions spread throughout memory and did not access these in ways that a compiler could predict and optimize would be slower, perhaps.
On some level, the number of banks you have is the number of different libraries you can easily have available at the same time. (Less one for the program itself, and at least one for the program's data).

You're basically encountering the reasons that people went to a 32-bit world for "real" machines. Psychopathicteen's suggestion for native hardware long pointer load/store will help with this a little ... you free up one or two banks formerly for data, but code banks will still be a limitation.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: YCPU: an imaginary 16-bit processor.

Post by psycopathicteen »

How about just making the program counter be 24 bit, with long jumps and long returns?
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: YCPU: an imaginary 16-bit processor.

Post by pops »

I've been thinking about jump / load / store long instructions ("long access"). I think a very reasonable addition would be to have the requested bank index in R0 for these instructions. If the OS is handling bank allocation, each process will have its own list of bank indexes (range from $0000-$FFFF), and the compiler will allocate data/text segments and generate long access opcodes as appropriate. These instructions will raise a 'long access' interrupt, and the OS will have to use R0 and the upper bits of the generated address to place the appropriate bank in virtual memory.

Broken out:
  1. At some point during initialization, a process informs the OS that it is requesting a new bank of free memory, which it will refer to as bank $20.
  2. The OS reserves HW device $02, bank $05 for this bank.
  3. During execution, the process executes a 'jump long' instruction with R0 = $20 and Address = $F040.
  4. YCPU raises 'long access' interrupt with R0 = $20 and P2 = $F040.
  5. OS checks its list of banks for this process. This process stores it's bank $20 in HW device $02, bank $05.
  6. OS uses MMW instructions to switch in this bank in virtual address space $F000-$FFFF.
  7. OS transfers control back to the process.
This would be a fairly expensive operation in terms of cycles, but it would allow access to 32-bits of physical address space while maintaining 16-bit registers.
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: YCPU: an imaginary 16-bit processor.

Post by lidnariq »

I think you've just described a page fault, albeit one with specific semantics. It's not so different than the traditional desktop "trying to load data/execute code that has been paged to disk", only you don't need to stall the process while the kernel loads the data back into RAM.

(At one point for a class assignment I was tasked with designing an MMU, and I chose the lazy/minimalist way out: the PC had a minimum possible value, a maximum possible value, and if anything caused it to go out of bounds it would fault. All loads and stores would fault. Needless to say, my instructor was not pleased.)


I suspect the overhead of faulting on every access isn't really acceptable for data, though.
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: YCPU: an imaginary 16-bit processor.

Post by pops »

Your story made me laugh, and that took the sting out of the realization that my long load/store idea was rather ridiculous.
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: YCPU: an imaginary 16-bit processor.

Post by pops »

Implementing these opcodes is a pain in the butt. I'm regretting not designing a RISC processor. ;) I spent two hours today trying to figure out the tricky math that allows bit shifting --- through the carry flag, ignoring the carry flag, etc., etc., etc.

There were plenty of errors in the spec with regards to flag conditions, which have been fixed. I've updated the spec to 0.2f, which reflects these minor changes.
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: YCPU: an imaginary 16-bit processor.

Post by pops »

I've updated the specification to 0.2j. At this point I'm satisfied with the specification, and barring the discovery of additional errors, I don't see anything changing. Progress on the emulator has been slow, but as of this morning I have the entire thing implemented in software, except for the RTC, Hardware bus, and raising the 'UnPriv Opcode' interrupt while in User mode.

In this update are a complete specification for the RTC, updates to the MMU, and new instructions that will help with changing the context of the processor.

I added 'P' present and 'A' accessed bits to help with bank management.
2.F.3. THE MMU CACHE wrote:

Code: Select all

    A - Access flag:
        This bit is set every time this bank is written to.
    P - Not present flag:
        If this bit is set, any access to this bank will raise a bank fault.
I specified the state of the U, W, and E bits on bank fault:
2.F.5. BANK FAULTS wrote:

Code: Select all

When a bank fault is raised, the processor status bits U, W, and E will describe
the operation that caused the bank fault:
        U W E   Bank Fault Type
        0 0 0   Bank not present
        0 0 1   Attempted execution of execute-protected memory.
        0 1 0   Attempted supervisor write of write-protected memory.
        1 0 0   Attempted user read of supervisor-only memory.
        1 1 0   Attempted user write of supervisor-only memory.

Note that writes to ROM banks will fail silently unless the MMU is enabled and
the write-protect bit is set, which will generate a bank fault.
Specification for the RTC. You get the current time using the HWQ instruction, and can enable/disable the interrupt, as well as specify the interrupt frequency of the CLOCK interrupt.
2.H. The Real Time Clock wrote:

Code: Select all

The processor contains an integrated Real Time Clock (RTC). The RTC maintains
and updates its internal time data indefinitely, even when the processor is
powered down. The processor can query the RTC for the current time.

The clock provides time data with the following precision:
    Year:    8 bits (0-255)
    Month:   4 bits (0-11)
    Day:     5 bits (0-31)
    Hour:    5 bits (0-23)
    Minute:  6 bits (0-59)
    Second:  6 bits (0-59)
    Tick:   16 bits (0-65535)
    
The RTC also provides a CLOCK interrupt, which can be enabled, disabled, and the
frequency of which can be specified. When the RTC interrupt is enabled, the RTC
will raise the CLOCK interrupt at an interval which is specified by the
processor. The RESET interrupt disables the RTC.
The new 'JCX' instruction allows the processor to change the entirety of the memory space and jump into user mode:
3.H. Jump & Call Instructions wrote:

Code: Select all

JCX         Unconditional Jump and change Context
    1. A pointer to a 32-word array of MMU data is popped from the stack.
    2. The MMU is loaded with the data in the 32-word array.
    3. The value iPS is popped from the stack.
    4. The remaining registers are popped from the stack in this order:
       SP, R6, R5, R4, R3, R2, R1, R0, FL, PC
    5. PS is set to iPS.
    6. Execution continues.
    
    NOTE: The following operations are the equivalent of 'save context':
    PSH R7, R6, R5, R4, R3, R2, R1, R0, FL, PC
    TSR R0, PS
    PSH R0
    ; determine the address where the 32-word MMU cache will be saved.
    LOD R0, {Address of 32-word MMU cache store location}
    MML R0
    PSH R0
One other big change: I've though a ton about how interrupts should work. The supervisor memory state should be switched into memory so it can handle the interrupt, but the previous memory state should be maintained. Here's my solution:

On an interrupt, the processor saves the current PS, enters supervisor mode, and disables the MMU. The memory space is as follows:

Code: Select all

While the 'Memory Banking' status bit is clear, the MMU is disabled, and the
processor's address space is mapped as follows:[quote="2.F.1. ADDRESS SPACE WHEN THE MMU IS DISABLED"][code]    Bank $0 - If R status bit is clear, Internal ROM Bank $00
              If R statis bit is set, Internal RAM Bank $00
    Bank $1 - Internal RAM Bank $01
    Bank $2 - Internal RAM Bank $02
        ...
    Bank $E - Internal RAM Bank $0E
    Bank $F - Internal RAM Bank $0F
Because this is a known state, the OS can plan out what data and routines it needs to have in memory to handle interrupts - and has the entire address space available to put this data in. Because the MMU cache hasn't been changed, the processor can either leave it unchanged and the pre-interrupt memory space will be 100% restored when the M status bit is set again, or save the MMU cache (easy to do with MML/MMS) if the OS needs to load different memory space.
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: YCPU: an imaginary 16-bit processor.

Post by lidnariq »

pops wrote:A - Access flag:
This bit is set every time this bank is written to.
Other ISAs call this the "dirty" flag/bit, where it indicates that the line of cache hasn't been flushed back to system RAM (which is mostly only a problem for SMP).
Specification for the RTC. You get the current time using the HWQ instruction, and can enable/disable the interrupt, as well as specify the interrupt frequency of the CLOCK interrupt.
It might be clever to arrange the indices such that the set that can be called from user mode are contiguous.
Note that the IBM PC had two separate "RTC"s; The original was an 8253 programmable timer, which allowed for interrupts at 13.125MHz÷11÷n, where 1≤n≤65536. But it didn't store the value while power was off, and had a minimum frequency of 18Hz. The other was added with the 286 AT, and is something equivalent to the MC146818, a conventional RTC, so it's only programmable to interrupt frequencies at powers of 2, 1 to 8192 Hz, as well as 1/min and 1/hr.
Modern PCs have added what they call an HPET because the 18 Hz minimum of the 8253, and the coarseness of the RTC's interrupts, didn't allow for the full range of sleep periods OS kernels wanted for reduced power consumption.
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: YCPU: an imaginary 16-bit processor.

Post by pops »

I used my spring break to complete the first draft of the emulator, hosted at Github, which I'm releasing under the MIT license. It is a 99% implementation of the current specification - every single opcode, the MMU, interrupts, and so on. It also includes an integrated debugger. It runs at around ~40mhz on single-threaded 1.3Ghz Core i5, Windows 7 hosted in Parallels 9/OS X 10.9 - no speed demon, but no slowpoke, either.
Screen Shot 2014-03-23 at 11.08.38 PM.png
The emulator currently fills the memory with random words and executes the same. It does not include an assembler, or any way to load data into memory. Those are pretty essential parts of the puzzle, but they'll have to wait until I have some more free time.

Thanks again to everyone who helped me flesh out the spec. I had a ton of fun designing and implementing the YCPU!
lidnariq wrote:Other ISAs call this the "dirty" flag/bit, where it indicates that the line of cache hasn't been flushed back to system RAM (which is mostly only a problem for SMP).
That was exactly the idea I had for it. I wanted to give a developer some indication that a bank of memory had been edited - in case the developer was using a slow storage device for additional virtual memory.
It might be clever to arrange the indices such that the set that can be called from user mode are contiguous.
Quite! I've chosen to order them by function however - all the jumps together, for example. Is it more common to keep unprivileged opcodes together than to order by function?
Post Reply