6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Wed Sep 25, 2024 11:24 am

All times are UTC




Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Fri Aug 27, 2021 5:26 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8395
Location: Midwestern USA
This topic might be of interest to anyone planning on building a 65C816 system with more than 64 kilobytes (KB) of memory. The discussion assumes the 816 is running in native mode.

In my POC units up through V1.2, there was never more than 64 KB of address space. I wasn't generating the A16-A23 address bits, so the 65C816 could not "see" anything beyond $00FFFF. The upper limit of accessible RAM was at $00CFFF in V1.0 and V1.1 (for a total of 52 KB), and at $00BFFF in V1.2 (for a total of 48 KB), with everything else being ROM and I/O. In 65C816-speak, everything is in bank $00.¹ Borrowing a term from PC-compatible architecture, bank $00 RAM is "basic RAM." It's "basic" because there has to be some RAM in bank $00 in order to have a basic running system.

POC V1.3, as well as the soon-to-be-built POC V2.0, has a bank $00 memory map that is the same as V1.2, and also has RAM from $010000 to $01FFFF. In 65C816 terms, that is bank $01 RAM. Again borrowing a PC architecture term, RAM in bank $01 (and higher) is "extended" memory. Hence V1.3 has one bank of basic RAM and one bank of extended RAM.

Future versions of the V2 series will have more extended RAM. For example, I'm planning on installing 256 KB in POC V2.1, which means it will have three banks of extended RAM. POC V2.2 will get 512 KB of RAM, giving it seven banks of extended RAM. Carrying this farther, a 65C816 machine built with one of Garth's 4 MB DIMMs would have 63 banks of extended RAM.

Considering that the amount of extended RAM will be a variable, but the bank $00 memory map will fixed, it would be nice if I didn't have to create firmware specific to each POC V2 version and its available extended RAM. Ergo a means of sizing extended RAM during power-on, self-test (POST) will be a requirement in the firmware.

As I envision it, the sizing process would be in three steps:

  1. Conduct a non-destructive, detailed test of basic RAM, which purpose is to detect any bad RAM and if found, alert the user. I have working code for this step already developed and in use in all current POC units.

  2. Determine how many extended RAM banks exist in the machine. This information will be used by the next step, which will be to...

  3. Conduct a non-destructive, detailed test of every byte in every detected bank to establish the addressing limits of the system, as well as to detect a bad piece of RAM.

I have an idea on how to carry out step 2 (above), but have not written any code. Instead, I'm going to ask those who are reading this to submit some ideas on how to carry out that step. Keep in mind that the method must be non-destructive, unless a bad RAM location is detected, in which case the machine would be halted—an overwrite of RAM would be inconsequential. Also, the 816 will be running in native mode, which will actually make it easier.

I'll offer two clues on how it might be done.

  1. Address space from $00D000 to $00FFFF is ROM and therefore immutable.

  2. ROM (and I/O) will never be visible in any bank other than $00.

Let's see what you guys can conjure.

——————————
¹For the benefit of anyone not familiar with 65C816 addressing, a bank is the same as the A16-A23 address bits pattern. Hence the size of a bank is 64 KB and the maximum possible number of banks is 256.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 8:00 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
You wish to non-destructively test up to 4MB? So we likely have a (read, modify, write, compare, write, loop) times ~4 million bytes or ~2 million words? That's gonna burn some significant cycles either way ...

I'm certain you already have an idea of how you want to do this, so may I request that you post a first attempt and let us fiddle with it to see if we can make it more efficient?

I did some musing of my own awhile back, but I was firmly in 8-bit land when I was thinking about it.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Fri Aug 27, 2021 2:51 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 8:22 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
I think one would initially only test a byte or a few bytes at the start of each bank: the question to be answered is whether it's RAM, and whether it's a bank previously encountered.

To know that something is RAM, it's necessary to write to some byte a value different from the present value, and determine that the new value has been stored. To make that non-destructive, it's necessary to save the value first read, in order to restore it afterwards.

So, I think I'd use a table in the bank 00 RAM - already tested, or assumed to be present - to hold the 255 bytes from each of the 255 possible high banks. Then write the bank number to each bank's first byte, check that worked, then write the inverse and check that, and finally restore the initial bytes from my table, to make the process non-destructive. (Only need to restore to the unique banks of working RAM.)

RAM-testing each bank could be viewed as a similar exercise of divide and conquer.


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 11:13 am 
Offline

Joined: Wed Jun 23, 2021 8:02 am
Posts: 166
How about this:
Write 0 to address 0. Set address to be tested (=P) to 0x010000. Write 0x55 to P, read it back and check it - if it's not 0x55, there is no memory at P. Write 0xAA to P, read back and check it - if it's not 0xAA, there is no memory at P. Now read address 0 - if it's not 0, P is a duplicate of address 0 (address line A16 is not connected to RAM). If it is 0, repeat the entire process with P = 0x020000, then 0x040000 etc (check address lines A17 to A23 are connected to RAM). The loop terminates when you find either an unmapped power of 2 address, a power of 2 address which is mirrored to address 0, or you reach 0x800000 and find it's valid unique RAM. At this point, if you assume the RAM size is a power of 2 (which is often the case), you know the RAM size.


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 11:20 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1382
Well, you already have code that will perform a RAM test. Beyond Bank $00, testing is done in 64KB (banks). If you're still using the Maxim Realtime Clock, there's 256 bytes of NVRAM there. Why not use part of that NVRAM for configuration data? Add in a simple checksum byte to validate the (NVRAM) config data on cold start, then read out the config data for setup, or invoke a config routine to set the NVRAM.

You can include the actual memory configuration as well as default startup values for the four serial ports, which port is the console, etc., and can also use it for storage configuration information when you add that later.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 5:06 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
I think floobydust may have the most efficient plan ... the user would be expected to run the RAM test periodically instead of at every cold boot.

Riffing on the ideas of the other participants here, there's a way to test each location non-destructively without actually saving the original value and restoring it from "known-good" RAM ... if you EOR the value at a location with one or more portions of the address of that location, store it and EOR it back to original after it responds properly.
Code:
    lda [ptr]
    sta temp
    eor #$ff
    eor ptr
    eor ptr+1
    eor ptr+2
    sta [ptr]
    lda [ptr]
    eor #$ff
    eor ptr
    eor ptr+1
    eor ptr+2
    cmp temp
    bne ruh_roh_
    sta [ptr]
...

To avoid the mirroring possibility, you would have to alter all of the locations first and then test and restore them from the opposite direction. But ... that would still require stashing all the original values, wouldn't it? Doh! Sounds like a job for a big BANK 0 stack ...

I think there's much to be gained by the knowledge and avoidance of going through the hassle of testing the configurations which can't exist on your hardware.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Fri Aug 27, 2021 7:37 pm, edited 6 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 6:49 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
I think catering for various missing and mirroring and not-power-of-two sizes will be quite the challenge. Catering for all possible dataline and address line shorts is another thing. Let alone actual pattern-sensitive failure modes.

I think testing for size is best handled as a different problem from testing for system level faults which is again different from testing for failures within RAM chips.


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 10:09 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8395
Location: Midwestern USA
Thanks to those who have so far offered suggestions.

barrym95838 wrote:
You wish to non-destructively test up to 4MB? So we likely have a (read, modify, write, compare, write, loop) times ~4 million bytes or ~2 million words? That's gonna burn some significant cycles either way ...

I only presented the 4 MB as a "fer instance." As the 65C816 can address 16 MB, the possibility of testing millions of bytes is there. It would ultimately depend on how important it is to know that all memory is good at power-on. However, the millions of bytes being tested wouldn't mattter in determining how many banks of extended RAM are present—at most, two memory cells per bank could be involved.

As an aside, the 65C816 has very good compute-bound performance, especially if use of REP and SEP (three cycles each) can be avoided during memory testing. I envision that a test of megabytes of RAM would run at a reasonable speed, assuming a high clock speed is in use.

Quote:
I'm certain you already have an idea of how you want to do this, so may I request that you post a first attempt and let us fiddle with it to see if we can make it more efficient?

Right now, I've only a half-baked idea of how to accomplish this. At the moment, I don't want to go into any more detail on my thoughts (such as they are) lest I unduly influence others' thinking. I will, however, offer one more clue, which could be important in devising a suitable algorithm. Commonly-available, large SRAM is produced in even multiples of 64 KB, with 128 KB and 512 KB pieces being the ones I would be using in my designs (for now—larger SRAM is only available in 3.3 volts).

BigEd wrote:
I think one would initially only test a byte or a few bytes at the start of each bank: the question to be answered is whether it's RAM, and whether it's a bank previously encountered.

To know that something is RAM, it's necessary to write to some byte a value different from the present value, and determine that the new value has been stored. To make that non-destructive, it's necessary to save the value first read, in order to restore it afterwards.

That would be the method I'm considering, although I was going to test at the high end of the bank where the test cells (I'd be using 16-bit test patterns) would be ROM if the bank being probed mirrors to bank $00.

Quote:
So, I think I'd use a table in the bank 00 RAM - already tested, or assumed to be present - to hold the 255 bytes from each of the 255 possible high banks. Then write the bank number to each bank's first byte, check that worked, then write the inverse and check that, and finally restore the initial bytes from my table, to make the process non-destructive. (Only need to restore to the unique banks of working RAM.)

That would work and definitely prove that a bank is present...or not. However, it would be destructive. In developing the memory test for bank $00 (which occurs prior to the first visible signs of activity), I decided that everything in the range $000200-$00BEFF should survive a reboot so evidence could be preserved when I hit reset to recover from a crash. The only bank $00 address ranges that are destructively tested are the physical zero page, $0100-$01FF (system vectors and other critical data), and the native mode stack, which is presently at $BF00-$BFFF, the highest page of accessible bank $00 RAM in POC V1.3, and initially in V2.0.

kernelthread wrote:
Write 0 to address 0. Set address to be tested (=P) to 0x010000. Write 0x55 to P, read it back and check it - if it's not 0x55, there is no memory at P. Write 0xAA to P, read back and check it - if it's not 0xAA, there is no memory at P. Now read address 0 - if it's not 0, P is a duplicate of address 0 (address line A16 is not connected to RAM)...

Unfortunately, writing "0 to address 0" would be destructive, as that would step on a direct page location used by the IRQ handler, which will already be running when the probe for extended RAM banks will occur. A different location could be used, however, since direct page above $51 will be unused during POST and could be overwritten without consequence. For efficiency reasons, I'd be using a 16-bit test pattern, i.e., $A55A, followed by $5AA5, but otherwise your method would work.

floobydust wrote:
Well, you already have code that will perform a RAM test. Beyond Bank $00, testing is done in 64KB (banks). If you're still using the Maxim Realtime Clock, there's 256 bytes of NVRAM there. Why not use part of that NVRAM for configuration data?

Storing RAM configuration information in NVRAM won't prove that the RAM is usable. It will, however, speed up POST, since the bank probe wouldn't be required and the detailed memory test could be skipped (modern PCs do this when a fast boot is configured in the BIOS).

However, what if NVRAM gets corrupted due to a wild write in an ill-behaved program (which has happened several times :oops:), or what if I replace the RTC with a different one having no data in NVRAM? In either case I'd still have to do the bank probe to determine what is present. Also, not doing the bank probe on each boot cheerfully assumes that all banks are present, even if a piece of memory goes south for some reason.

barrym95838 wrote:
Riffing on the ideas of the other participants here, there's a way to test each location non-destructively without actually saving the original value and restoring it from "known-good" RAM ... if you EOR the value at a location with one or more portions of the address of that location, store it and EOR it back to original after it responds properly.

That is a good—although slightly slower—alternative to the read-save-write-compare-write-compare-restore-compare sequence, as it would have the somewhat-beneficial effect of "exercising" the test locations' bits a little more than the alternative procedure. However, using a bank $00 stack to preserve things would itself be destructive, which can't happen for the reasons I explained above.

BigEd wrote:
I think catering for various missing and mirroring and not-power-of-two sizes will be quite the challenge. Catering for all possible dataline and address line shorts is another thing. Let alone actual pattern-sensitive failure modes.

I think testing for size is best handled as a different problem from testing for system level faults which is again different from testing for failures within RAM chips.

Good points. The bank discovery probably would report a bank not present if there was an address or data bus fault, but that would depend on which address or data line is faulty and where in the bank the test is occurring.

The detailed test would not be exhaustive and thus wouldn't necessarily detect a hardware fault. Mostly it would be to prove that the memory cells being tested will be able to store and regurgitate the test patterns. Should the need arise to fully qualify all memory locations, as well as address and data bus operation, a much more complex (and much slower) test regimen would be required to do inversions, walking bits, etc. I wouldn't do that in POST due to the time required.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 27, 2021 11:04 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
BigDumbDinosaur wrote:
As an aside, the 65C816 has very good compute-bound performance, especially if use of REP and SEP (three cycles each) can be avoided during memory testing.

Yeah, I was checking out the firmware you wrote for one of your POCs, and you spent a not-insignificant chunk of your executable REPing and SEPing. I'm not knocking it, just making a friendly observation.

So ... anyway, how many bytes of stack are you willing to allocate (sacrifice) to the memory sizing and testing routine? 256? 16? Zero? None of the above? If you're only doing the test as part of a POST, why are you concerned about preserving the contents of RAM that just woke up?

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 28, 2021 12:51 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8395
Location: Midwestern USA
barrym95838 wrote:
BigDumbDinosaur wrote:
As an aside, the 65C816 has very good compute-bound performance, especially if use of REP and SEP (three cycles each) can be avoided during memory testing.

Yeah, I was checking out the firmware you wrote for one of your POCs, and you spent a not-insignificant chunk of your executable REPing and SEPing. I'm not knocking it, just making a friendly observation.

No question there are more than a few REPs and SEPs in there. I periodically look at the code and try to find ways to reduce that, but in the monitor, especially, there seems to be a constant need to jump between handling words and bytes, especially the latter for I/O purposes.

Quote:
So ... anyway, how many bytes of stack are you willing to allocate (sacrifice) to the memory sizing and testing routine? 256? 16? Zero? None of the above?

I could see allocating 256 by using one of the serial I/O (SIO) circular queues belonging to channel D. Only channel A has to be functional during POST, so the remaining circular queues could be stepped on without consequence.

Quote:
If you're only doing the test as part of a POST, why are you concerned about preserving the contents of RAM that just woke up?

I'm not concerned about preserving RAM when initially powering on, since that patently would be full of garbage. However, I do want to preserve as much RAM as possible following use of the reset button so I can conduct a post mortem when a crash occurs. Hence the requirement that testing be non-destructive.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 28, 2021 5:39 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
256 bytes ... that's enough to get some meaningful work done, I would hope.

It's likely that you've already thought of this, but you might consider having a warm RESET option in your firmware. Your reset code checks a byte (or a few) in volatile memory (or I/O registers) for a "unique" powered up signature, and if it sees it then it skips most of the POST and returns control to the command line quickly for a postmortem. If the signature is not correct, then it sets it "correct" and proceeds to a full, destructive (i.e. faster) RAM POST. By splitting this functionality, you get to where you want more quickly in both cases.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 28, 2021 6:33 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8395
Location: Midwestern USA
barrym95838 wrote:
It's likely that you've already thought of this, but you might consider having a warm RESET option in your firmware. Your reset code checks a byte (or a few) in volatile memory (or I/O registers) for a "unique" powered up signature, and if it sees it then it skips most of the POST and returns control to the command line quickly for a postmortem. If the signature is not correct, then it sets it "correct" and proceeds to a full, destructive (i.e. faster) RAM POST. By splitting this functionality, you get to where you want more quickly in both cases.

I have thought about it. It's a bit tricky with static RAM because unlike dynamic RAM, which powers up with $00 or $FF in its cells, SRAM powers up with any of 256 possible bit combinations randomly present in its cells. That characteristic could coincidentally produce the "powered-up" signature, although that's a bit of a stretch.

I've also looked at using register $0C in the 28L92 DUART as a place to store a warm-reset flag. With the DUART operating in x86 bus mode, register $0C is uncommitted and may be used as a one-byte scratchpad. At power-on, that register always comes up with the bit pattern %00001111. So I could arrange for the first stage POST to write a different bit pattern in that register after the DUART has been configured. Then, earlier in first-stage POST, that "flag" would be tested for the warm-reset bit pattern and if present, the RAM test could be partially or completely skipped.

I ended up setting aside that idea, since the DUART will revert that register to %00001111 following a hard reset. The hard reset would be needed to return the DUART(s) to a known state following a crash, since the crash may be triggered by a wild write to one of the DUARTs. So that is not a workable idea.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 28, 2021 6:45 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
Good points, all. I confess that I haven't completely familiarized myself with your project, but are NMI and/or ABORT somehow available for a quick "panic" escape from a crash to the command line? Another (possibly goofy) idea would be to wire your warm /RESET button just to the '816 and use a diode to isolate the signal from the other devices.

(I'm not trying to be lazy, even though it's my natural state. :roll: I'm just throwing out ideas that might give the firmware an "easier" job ... you know, "work smarter, not harder".)

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Sat Aug 28, 2021 7:13 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 28, 2021 7:11 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8395
Location: Midwestern USA
barrym95838 wrote:
Good points, all. I confess that I haven't completely familiarized myself with your project, but are NMI and/or ABORT somehow available for a "panic" escape from a crash to the command line?

I have a "panic button" wired to /NMI for that purpose. The NMI handler will respond by fetching the bank (PB) and program counter (PC) from the stack to determine where the MPU was when the panic button was pressed. If PB and PC indicate that the MPU was not executing something in ROM, control will be given to the M/L monitor (which is part of the firmware), thus providing the "escape route" needed to regain control.

What I described will work 99 percent of the time. The other one percent is when I do something truly stupid that accidentally points the direct page pointer and/or the stack pointer to ROM, the I/O block or some other place that will completely confuse the MPU. Even more entertaining is when a wild write in some code I'm testing trashes DUART #1's setup, causing the DUART to continuously hold /IRQ down and deadlock everything. In such cases, the only means of recovery will be with reset. Hence the need to avoid a destructive RAM test during POST so I can figure out what caused the machine to go off the road and smack into a big tree. :D

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 28, 2021 11:30 am 
Offline

Joined: Wed Jun 23, 2021 8:02 am
Posts: 166
BigDumbDinosaur wrote:
I have thought about it. It's a bit tricky with static RAM because unlike dynamic RAM, which powers up with $00 or $FF in its cells, SRAM powers up with any of 256 possible bit combinations randomly present in its cells. That characteristic could coincidentally produce the "powered-up" signature, although that's a bit of a stretch.

I've also looked at using register $0C in the 28L92 DUART as a place to store a warm-reset flag. With the DUART operating in x86 bus mode, register $0C is uncommitted and may be used as a one-byte scratchpad. At power-on, that register always comes up with the bit pattern %00001111. So I could arrange for the first stage POST to write a different bit pattern in that register after the DUART has been configured. Then, earlier in first-stage POST, that "flag" would be tested for the warm-reset bit pattern and if present, the RAM test could be partially or completely skipped.

I ended up setting aside that idea, since the DUART will revert that register to %00001111 following a hard reset. The hard reset would be needed to return the DUART(s) to a known state following a crash, since the crash may be triggered by a wild write to one of the DUARTs. So that is not a workable idea.[/color]


This is another advantage of using an ATtiny or similar as your reset supervisor instead of a DS1813. When the ATtiny resets, it can read an internal reset reason register to find out what caused the reset - power on, brownout, user reset or watchdog. It could then output the reason on a couple of GPIO lines for use by the main processor.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 28 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: