6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 11:52 pm

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 
Author Message
PostPosted: Mon May 30, 2016 7:36 am 
Offline
User avatar

Joined: Sun Oct 13, 2013 2:58 pm
Posts: 491
Location: Switzerland
Here my 5 cents about a 6502 System with MMU

after reading several threads and solutions of systems using some sort of MMU I thought it would be cool to build a system with a MMU. I'm perfectly aware that having a MMU
with a 6502 is only of academic interest and the 6502 is far away from supporting virtual memory. So this project is pure fun and interest in the subject.

Here is a draft if what I'm about to design. An MMU can serve several functions but I will restrict myself to

- virtual address
- kernel protection
- write protect pages (although in the first release it will only disable writes but no further actions are planned)

I will not use the MMU to support non-resident pages, RAM is cheap and the support for this is beyond this project. I will however consider the following features for a later design using a 65C816.

- abort on write protected pages
- abort on pages that are marked as no execute

I have decided to use a 74LS610 as the MMU. The reason I will use this device is that it is well known, simple, available in DIP and requires only very little GLUE. The maximal
supported memory will be 1Mbyte however I will start with a single 512kbyte SRAM. I will only use 8-bits of the mapping registers for the phyiscal address extensions. The
other 4 bits will be used to implement write protected (WP) pages and pages marked as no execute (NX). WP will be bit 11 of the MMU register and NX will be bit 10.

Principle of operation

After a reset the MMU is disabled (pass-through) and the system will present the following memory map

$0000..$DFFF RAM
$E000..$EFFF IO
$FFFF..$FFFF ROM

All the RAM is write enabled and execute enabled (WP=NX=0 as is the case in pass-through mode). IO is devided into 16 pages.

$E000..$E0FF MMU
$E100..$E1FF Console using a CDP6551 ACIA, I use this as all my ROM images with Monitor use this hardware, just to make my life easy
$E200..$E2FF UART (one of the famous Siemens UART with FIFO and multiple channels that BDD always praises)
$E300..$E3FF 8-bit IDE (CF-Cards support a 8-bit data mode)
$E400..$EFFF free for future use

The ROM will just contain a small monitor using the console together with a small boot-loader.

The major drawback of the 6502 and the 74LS610 is that they do not have a "support" to completely separate "user" and "kernel" mode. That is the 6502 does not
have a dedicated kernal stack-pointer and the 74LS610 has only one set of registers. On the other hand the WD65C02 has a nice feature, the VPB output that
is asserted when the processor reads a vector address. I will use this signal to reset the MMU enable flip-flop. So whenever the CPU reads a vector the MMU
is disabled. Also WP and NX bits will no longer be honored.

So whenever an IRQ, RES, BRK or NMI occurs the User PS and User PC (not in case of RES of course) are saved on the stack in the user address space,
the MMU is switched off and the vector is read from the ROM. Of course they point to some location in RAM (except for the RES vector), where previously
the interrupt handlers have been installed. Then we will store the user context and prepare Kernal mode.

STA USRA
STX USRX
STY USRY
TSX
STX USRX
LDX #KERNALSTACK
TXS
CLD and so on....


If required the MMU can be reactivated and the memory can be mapped to read whatever is required. To return to the usermode the following will
have to be done

- disable the MMU (in "kernal" mode the MMU can be switched on and off under program control)
- load MMU registers with user mapping of corresponding user task
- load the user context
- trigger MMU activation
- execute a RTI

The MMU trigger will delay activation of the MMU by two cycles. So the RTI is still fetched from the "kernel" mode but restoring the PC and PS is done
using the "user" mode with the "user" memory map and the PC and PS will be fetched from the "user" stack.

"user" mapping will typically only map RAM, the only way for a "user" program to exit is using the "BRK" instruction. "NMI" will take care of the
task scheduling and even if a program disables the interrupts (what it should normally not do without asking the "kernal" for permission) will give
the kernal the chance to correct things (e.g. check for pending interrupts, enable interrupts for the "user" program, switch the task etc.)

Of course a 65C816 would have further possiblities like additional vectors (COP) and the possibility to ABORT the instruction. On the other hand
a "kernel" needs to come up with additional support for NATIVE/EMULATION mode and 8/16-bit register mode.

Cheers

Peter


Top
 Profile  
Reply with quote  
PostPosted: Mon May 30, 2016 6:30 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8507
Location: Midwestern USA
cbscpe wrote:
I have decided to use a 74LS610 as the MMU.

Do you have a source for for the '610? Also, I assume you are aware that it is a TTL device and as is typical of most such silicon, has weak fanout and may not be able to reliably drive CMOS inputs. Also, it's relatively slow...

Quote:
After a reset the MMU is disabled (pass-through) and the system will present the following memory map

$0000..$DFFF RAM
$E000..$EFFF IO
$FFFF..$FFFF ROM

All the RAM is write enabled and execute enabled (WP=NX=0 as is the case in pass-through mode). IO is devided into 16 pages.

I seem to recall that the state of the '610 is undefined at power-on. How will you get it into the above state?

Quote:
$E200..$E2FF UART (one of the famous Siemens UART with FIFO and multiple channels that BDD always praises)

Actually, the UARTs I use are made by NXP. Possible candidates for your system would be the 26C92, 28L92 (the device in current use in POC V1.1) or the 28C94.

Quote:
On the other hand the WD65C02 has a nice feature, the VPB output that is asserted when the processor reads a vector address. I will use this signal to reset the MMU enable flip-flop. So whenever the CPU reads a vector the MMU is disabled. Also WP and NX bits will no longer be honored.

The WDC 65C02 also briefly asserts VPB during reset, so that might be the means by which you could put the '610 into a known state following a hard reset.

Quote:
So whenever an IRQ, RES, BRK or NMI occurs the User PS and User PC (not in case of RES of course) are saved on the stack in the user address space, the MMU is switched off and the vector is read from the ROM. Of course they point to some location in RAM (except for the RES vector), where previously the interrupt handlers have been installed. Then we will store the user context and prepare Kernal mode.

Code:
   STA      USRA
   STX      USRX
   STY      USRY
   TSX
   STX      USRX
   LDX      #KERNALSTACK
   TXS
   CLD and so on....


If required the MMU can be reactivated and the memory can be mapped to read whatever is required. To return to the usermode the following will
have to be done

- disable the MMU (in "kernal" mode the MMU can be switched on and off under program control)
- load MMU registers with user mapping of corresponding user task
- load the user context
- trigger MMU activation
- execute a RTI

The MMU trigger will delay activation of the MMU by two cycles. So the RTI is still fetched from the "kernel" mode but restoring the PC and PS is done using the "user" mode with the "user" memory map and the PC and PS will be fetched from the "user" stack.

Since you are planning on have a user stack and a kernel stack, why not just push the registers to the user stack when an interrupt hits, instead of writing them into RAM? Context changes would take a bit less code, would be faster and best of all, a common interrupt return (CRTI) could be used to switch context back to user mode upon completion of interrupt service, regardless of interrupt type.

Also, note that the 65C02 automatically reverts to binary mode when any interrupt is serviced, so the CLD instruction wouldn't be required.

Quote:
Of course a 65C816 would have further possiblities like additional vectors (COP) and the possibility to ABORT the instruction. On the other hand a "kernel" needs to come up with additional support for NATIVE/EMULATION mode and 8/16-bit register mode.

If it were me, I'd start right off with the 65C816. Also, I would not consider emulation mode at all, except maybe for initial experimentation as you sort out circuit bugs. Switching from native to emulation mode, especially during interrupt servicing, is fraught with peril, not the least of which is the 16 bit stack pointer suddenly becomes 8 bits and is hard wired to page 1 RAM. Is that really what you want?

Something to consider is that everything that the 74LS610 can do may be synthesized in a small CPLD (e.g., an ATF1504AS). Also, any CPLD you might use will be a CMOS device with much better fanout and much higher operating speed than the '610.

Just some curmudgeonly opinions...

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon May 30, 2016 8:18 pm 
Offline
User avatar

Joined: Sun Oct 13, 2013 2:58 pm
Posts: 491
Location: Switzerland
BigDumbDinosaur wrote:
cbscpe wrote:
I have decided to use a 74LS610 as the MMU.

Do you have a source for for the '610?

Yes I already have some and also some 74LS612.

BigDumbDinosaur wrote:
Also, I assume you are aware that it is a TTL device and as is typical of most such silicon, has weak fanout and may not be able to reliably drive CMOS inputs. Also, it's relatively slow...

Yes I know, but some pull-ups on the respective signals will help. The MMU takes about 70ns. I plan to have everything to settle in the PHI2 low phase, with a 14MHz W65C02 and a fast CPLD everything should be settled within approx 120ns. The initial plan was to run the system with either 4MHz or 3,6864 MHz. High-speed is not the design goal.

BigDumbDinosaur wrote:
I seem to recall that the state of the '610 is undefined at power-on. How will you get it into the above state?

There is the MM input. When set high the address inputs (MA0 to MA3) are passed through to the address pins MO8 to MO11 and address output pins MO0 to MO7 are low. I plan to have a MMU control register that has a defined state after reset and will set MM high to have a "neutral" mapping.


BigDumbDinosaur wrote:
cbscpe wrote:
$E200..$E2FF UART (one of the famous Siemens UART with FIFO and multiple channels that BDD always praises)

Actually, the UARTs I use are made by NXP. Possible candidates for your system would be the 26C92, 28L92 (the device in current use in POC V1.1) or the 28C94.

yes of course it's NXP don't ask me I always confuse them with Siemens but in fact they have been formerly Philips. My bad.

BigDumbDinosaur wrote:
The WDC 65C02 also briefly asserts VPB during reset, so that might be the means by which you could put the '610 into a known state following a hard reset.

In my design VPB will always set the Status Bit that controls MM to the high-state. So whenever the W65C02 reads a vector it is from the "kernel" mapping, that is when the MMU is disabled.
BigDumbDinosaur wrote:
Since you are planning on have a user stack and a kernel stack, why not just push the registers to the user stack when an interrupt hits, instead of writing them into RAM? Context changes would take a bit less code, would be faster and best of all, a common interrupt return (CRTI) could be used to switch context back to user mode upon completion of interrupt service, regardless of interrupt type.

Also, note that the 65C02 automatically reverts to binary mode when any interrupt is serviced, so the CLD instruction wouldn't be required.


This code executes after the vector has been read, MMU is then disabled and therefore the "user" address space is not accessible. STA zp / LDA zp require the same amount of cycles as a PHA / PLA pair. So no need to access a stack at that moment. Always keep in mind "kernel" mode always starts with MMU disabled, that is the 6502 address space is mapped one-to-one to the first 64kbyte phyiscal addresses. If the "kernel" needs to access user memory it will first have to set the MMU registers to map it's own requirements, e.g. it's own code and/or some user RAM and then activate the MMU again. In "kernel" mode the MMU can be enabled/disabled as required most other designs only disable it after a reset and when accessing the MMU it will be activated forever. Also because I need to switch into "user" mode I need to have some combined action which goes from disabled MMU to active MMU and at the same time performs an action in "user"mode. So I let the CPU fetch the RTI instruction from "kernel" and then activate the MMU, the rest of the RTI will be executed in "user" memory, that is it will pull PCL, PCH and P from the user stack and start execution from the loaded PC with the loaded status bits.


BigDumbDinosaur wrote:
If it were me, I'd start right off with the 65C816. Also, I would not consider emulation mode at all, except maybe for initial experimentation as you sort out circuit bugs. Switching from native to emulation mode, especially during interrupt servicing, is fraught with peril, not the least of which is the 16 bit stack pointer suddenly becomes 8 bits and is hard wired to page 1 RAM. Is that really what you want?

Something to consider is that everything that the 74LS610 can do may be synthesized in a small CPLD (e.g., an ATF1504AS). Also, any CPLD you might use will be a CMOS device with much better fanout and much higher operating speed than the '610.

Just some curmudgeonly opinions...


My design does not have severe stack limitations. Each User has it's own memory. Memory Page 0 is different for "kernel" and every "user" address space. In fact there is no "shared" address, although you can setup the MMU to share some memory of course. Only "kernel" address $0xxx is actually $00xxx. And we all know 256bytes of stack for a single task is far enough. Also when the system jumps to "user" mode the "kernel" stack is always "clean"
To synthesize a 74LS610 you need a rather large CPLD, don't forget we need at least sixteen 8-bit registers, with WP and NX bits we need another 2 bits per memory page. So in total we need 160bits of storage and even a ATF1508 has only 128 flip-flops.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 18 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: